Data.Proxy in servant's public API (为什么 Proxy with ScopedTypeVariables 不起作用)

Data.Proxy in servant's public API (why Proxy with ScopedTypeVariables doesn't work)

我很兴奋 servant, and I'm ok with its internal typelevel magic as long as it doesn't gets in the way, and the only thing that confuses me is its use of a type-proxy in public API. Here's the code:

serve :: HasServer layout => Proxy layout -> Server layout -> Application
serve p server = toApplication (runRouter (route p (return (RR (Right server)))))

据我了解,类型代理是相当简单的事情,当您没有该类型的值时,需要携带该类型。那么为什么在Server的type参数中已经有布局类型的情况下,还要传递一个代理携带布局类型呢?我试过克隆仆人的回购并将代码更改为:

{-# LANGUAGE ScopedTypeVariables #-}

serve :: forall layout . HasServer layout => Server layout -> Application
serve server = toApplication (runRouter (route p (return (RR (Right server)))))
  where
    p :: Proxy layout
    p  = Proxy

令我惊讶的是,这无法编译(说明布局类型不匹配)。但是,为什么我的本地 p 不应该具有与服务器相同的布局类型(启用 ScopedTypeVariables)?

Server 不是类型构造函数,它是 HasServer class 中 ServerT 关联类型的类型同义词。因此 layout 出现在 Server layout 中不会消除 HasServer 约束的歧义。如果我们已经有一个 HasServer layout 实例,我们只能解析 Server layout 类型。

类型族不是单射的,Server后面有一个。因此,如果您将 Server MyAPI 传递给此函数,GHC 将无法得出 layout = MyAPI 的结论。不幸的是,我们确实需要一个 Proxy 。即使我们有单射类型族,也无济于事:

type API1 = Get '[JSON] User
type API2 = Post '[JSON] User

这两个API是这样Server API1 = Server API2但是API1 /= API2,这实际上告诉我们Server 一定不能内射。消除我们想要定位的 API 类型歧义的最简单方法是 Proxy。另一方面,这通常是我们 API 在各种仆人套餐中唯一要求的东西。