Haskell Servant:使用泛型时如何给路由加上前缀?
Haskell Servant: How to prefix routes when using generics?
我正在使用 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
" 前缀更改为 "backend/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
我正在使用 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
" 前缀更改为 "backend/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