我正在使用 Servant 泛型,并且我的路由有一个数据类型:
data Routes route = Routes
{ getLiveness :: route :- GetLiveness,
getReadiness :: route :- GetReadiness,
getAuthVerifyEmailToken :: route :- GetAuthVerifyEmailToken,
postAuthEmail :: route :- PostAuthEmail,
...
}
deriving (Generic)
type BackendPrefix = "backend"
type AuthPrefix = "auth"
type GetLiveness = BackendPrefix :> "liveness" :> Get '[JSON] Text
type GetReadiness = BackendPrefix :> "readiness" :> Get '[JSON] Text
type GetAuthVerifyEmailToken = AuthPrefix :> "verify" :> "email" :> Capture "token" JWT :> RedirectResponse '[PlainText] NoContent
type PostAuthEmail = AuthPrefix :> "email" :> ReqBody '[JSON] AuthEmailRequest :> PostNoContent
前两个使用相同的前缀"backend"
,而其他的都有一个"auth"
前缀。
但是,我现在想将"auth
"前缀更改为"后端/身份验证"。所以我尝试了查点:
type AuthPrefix = BackendPrefix :> "auth"
这会导致错误
> • Expected a type, but
> ‘"auth"’ has kind
> ‘ghc-prim-0.6.1:GHC.Types.Symbol’
> • In the second argument of ‘(:>)’, namely ‘"auth"’
> In the type ‘BackendPrefix :> "auth"’
> In the type declaration for ‘AuthPrefix’
> |
> 34 | type AuthPrefix = BackendPrefix :> "auth"
> |
所以我用谷歌搜索,发现你可以在不使用通用时做到这一点,你可以这样做:
type APIv1 = "api" :> "v1" :> API
但是我不知道如何使用泛型来做到这一点。
我想这留下了两个问题:
- 上述错误是什么意思,我可以使用类似
type AuthPrefix = BackendPrefix :> "auth"
的东西来创建更复杂的前缀吗? - 在 Servant 中使用泛型时,有没有办法用一个前缀前缀给某些路由加上前缀,而用不同的前缀为其他路由添加前缀?
:>
的定义是
data (path :: k) :> (a :: *)
注意右边的部分必须有种类*
,也称为Type
。这是一种"正常"的Haskell类型,如Int
或Bool
,已经提升了价值。
但是,在类型级表达式BackendPrefix :> "auth"
"auth"
的类型是Symbol
,这是类型级字符串的类型。种类不匹配,这会导致类型错误。
(你可能想知道,为什么像"foo" :> "bar" :> Post '[JSON] User
这样的路径会起作用?原因是完全应用的Post
具有善良Type
,完全应用的:>
也有善良的Type
,并且:>
与右边相关联,如"foo" :> ("bar" :> Post '[JSON] User)
,所以这一切都检查出来。
解决方案是什么?也许给AuthPrefix
一个类型参数,比如
type AuthPrefix restofpath = BackendPrefix :> "auth" :> restofpath
这样我们就不会在Symbol
中结束路由片段。
我们现在必须以不同的方式使用AuthPrefix
:
AuthPrefix ("email" :> ReqBody '[JSON] AuthEmailRequest :> PostNoContent)
这是因为新定义已经处理了将:>
应用于路径的其余部分。
:>
是infixr 4
的,这意味着它是右关联性的。考虑以下类型:
type GetFoo = "backend" :> "auth" :> "foo" :> Get '[JSON] Text
它被解释为好像你像这样用括号括起来:
type GetFoo = "backend" :> ("auth" :> ("foo" :> Get '[JSON] Text))
使用您尝试的类型同义词,它将被括起来,如下所示:
type GetFoo = ("backend" :> "auth") :> ("foo" :> Get '[JSON] Text)
这显然不是一回事,事实上甚至无效。
为了帮助理解,请考虑以下没有高级类型的普通Haskell代码:
xs = 1:2:3:4:5:6:[]
ys = 1:2:4:8:16:32:[]
现在想象一下你试图写这个:
zs = 1:2
xs = zs:3:4:5:6:[]
ys = zs:4:8:16:32:[]
它不起作用的原因与您不能拥有类型同义词的原因完全相同。
最后一个例子:
x = 2 ^ 3 ^ 2 -- evaluates to 2 ^ (3 ^ 2) = 512
y = 2 ^ 3
x = y ^ 2 -- evaluates to (2 ^ 3) ^ 2 = 64