我们可以用开放型系列完成类型级别“Scrap Your Boilerplate”吗?

时间:2015-12-01 15:25:53

标签: haskell types type-families

是否可以定义一个对改进开放的总类型系列?或者,我们可以在类型级别“废弃样板”(还有更多)吗?

我的意思是这是一个类型系列,它具有基本情况

type family F a
type instance F a = a

但允许细化

-- later
type instance F X = Y

这显然是重叠的,并且不允许重复类型系列用于类型安全。然而,这个很好的属性是F可以应用于任意类型,忽略大多数类型而只是“触发”某些重新索引。

可能没有直接答案。在这种情况下,这是我的用例。或许可能有不同的答案。

我想为Servant创建一个转换API声明的函数。对于那些不熟悉Servant API声明的人来说,它们是一个类似于以下规则构建的类型(但是可以通过新规则扩展)

x is a Symbol       |- Seg x is an API     (representing a static segment in the URI)
m is a Symbol       |- Capture m is an API (capturing data from the URI)
m is a Symbol       |- Header m is an API  (pulling in header info)
x and y are APIs    |- x :<|> y is an API  (biased choice)
x and y are APIs    |- x :> y is an API    (segment concatenation)
m is an HTTP method |- Verb m is an API    (endpoint, leaf)

这是一个松散类型的树,可以更具体,但你明白了。

Header "X-API-Version" :> "users" :> Capture "id" :> 
  (     Verb GET
   :<|> Verb POST
   :<|> Verb DELETE )

我想使用以下规则扩展此API树

 |- OptionsStub is an API
 ms is a list of Symbols |- Options ms is an API

其中Options msOPTIONS的端点,它报告CORS头Access-Control-Allowed-Headers,描述允许在此端点使用的所有请求头的集合。如果一个树中包含一些OptionsStub个端点,我可以推送Header我看到的树,并用OptionsStub类型替换Options类型。

Header "If-Range" :> 
Seg "users" :> 
(      OptionsStub 
  :<|> Verb Get 
  :<|> Capture "id" ( OptionsStub :<|> Verb Get ) )

变为

Header "If-Range" :> 
Seg "users" :> 
(      Options ["If-Range"]
  :<|> Verb Get 
  :<|> Capture "id" ( Options ["If-Range"] :<|> Verb Get ) )

围绕Options的演示启用本地推理。

type family Push hdrs api

-- interesting rules
type instance Push hdrs OptionsStub = Options hdrs
type instance Push hdrs (a :> b) = a :> Push (Pull a ++ hdrs) b

-- boring, but for completeness
type instance Push hdrs (Verb x) = Verb x
type instance Push hdrs (Seg s) = Seg s
type instance Push hdrs (Capture m) = Capture m
type instance Push hdrs (Header m) = Header m
type instance Push hdrs (x :<|> y) = Push hdrs x :<|> Push hdrs y

type family Pull api :: [*]

type instance Pull (Header m) = '[m]
type instance Pull a = '[]  -- OVERLAPPING

至关重要的是,我可以通过对有效API的类型进行完整分析来定义Push,但我决定不对Pull执行此操作,因此我遇到了重叠的实例。< / p>

更现实地说,重叠实例是可取的,因为API作为一种开放式扩展(或者实际上,它们是单一的,但在实践中具有相同的效果)而且我的Header收集系统是固定的。据推测,没有人会用一种表示请求标题的新方式扩展API类型(一种已经存在!)所以我的代码也可以关闭以进行扩展。

但是,如果我使用封闭式系列,那么就像这个问题一样,类型精化将会卡住。

我更喜欢简单地指定我的类型系列

type instance Push hdrs OptionsStub = Options hdrs
type instance Push hdrs (a :> b) = a :> Push (Pull a ++ hdrs) b

type instance Pull (Header m) = '[m]
type instance Pull a = '[]

然后允许用户扩展其API描述规则,而无需扩展这些类型系列的覆盖范围。

有没有任何机制可以实现这种目的?我想要它疯了吗?

0 个答案:

没有答案