英文:
Combining types for a servant endpoint
问题
以下是您要求的代码部分的翻译:
type MyAPI endpointTail result = "blah" :> Capture "a" A :> endpointTail :> Get '[JSON] result
MyAPI "hello" HelloT
MyAPI "hello/world" HelloWorldT
MyAPI ("hello" :> "world") HelloWorldT
type MyAPIF endpointTailF result = "blah" :> Capture "a" A :> endpointTail (Get '[JSON] result)
type Blah t = "hello" :> "world" :> t
MyAPIF Blah HelloWorldT
希望这些翻译对您有所帮助。如果您有其他问题或需要进一步的帮助,请随时提出。
英文:
Using servant, I've got a type like the following, but more complex:
type MyAPI endpointTail result = "blah" :> Capture "a" A :> endpointTail :> Get '[JSON] result
Which means I can do things like this:
MyAPI "hello" HelloT
but when I do:
MyAPI "hello/world" HelloWorldT
Servant silently fails to produce my endpoint correctly, presumably because it doesn't expect a literal slash
When I try:
MyAPI ("hello" :> "world") HelloWorldT
I get a type error because :>
is only defined when the right argument is of kind *
, which "world"
is not, it's of kind Symbol
.
It seems for servant
to work correctly, the :>
has to be applied in a right associative fashion, one can't just add brackets willy nilly. So what I think I need is something like this:
type MyAPIF endpointTailF result = "blah" :> Capture "a" A :> endpointTail (Get '[JSON] result)
Note endpointTail
is now a type function, endpointTailF
.
Then I could do
type Blah t = "hello" :> "world" :> t
MyAPIF Blah HelloWorldT
But now I've got the issue of the compiler saying it doesn't like Blah
being partially applied.
So in summary, I've got a nice reusable type
which I'd like to keep using, I'd just like to be able to pass more parameters. If these were values I'd be able to do this easily, by passing a function "continuation" style, but I'm not sure of the solution in the type world. Any ideas?
答案1
得分: 1
你的推断都是正确的。除非你想深入研究单例去功能化(通常用于处理部分应用类型函数的方法),否则你应该避免将类型函数视为参数。而是使用一个普通的数据结构。
举个例子,如果你编写一个类型函数,它使用 URL 路径段的列表作为前缀:
type PrefixWith :: [Symbol] -> Type -> Type
type family PrefixWith segs t where
PrefixWith (seg:segs) t = seg :> PrefixWith segs t
PrefixWith '[] t = t
然后在你的 API 中调用这个函数:
type MyAPI endpointTail result
= "blah"
:> Capture "a" A
:> PrefixWith endpointTail (Get '[JSON] result)
这使你能够编写以下代码:
MyAPI ["hello","world"] HelloWorldT
完整的代码示例:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
import Servant
import Data.Aeson
import Data.Kind
import GHC.Generics
import GHC.TypeLits
import Network.Wai.Handler.Warp
type PrefixWith :: [Symbol] -> Type -> Type
type family PrefixWith segs t where
PrefixWith (seg:segs) t = seg :> PrefixWith segs t
PrefixWith '[] t = t
type MyAPI endpointTail result
= "blah"
:> Capture "a" A
:> PrefixWith endpointTail (Get '[JSON] result)
data HelloWorldT = HelloWorld { success :: Int } deriving (Generic)
instance ToJSON HelloWorldT
type A = Int
main :: IO ()
main = run 8080 (serve (Proxy @(MyAPI ["hello", "world"] HelloWorldT))
(\n -> return (HelloWorld n)))
-- 例如的URL,http://localhost:8080/blah/1/hello/world
英文:
Your deductions are all correct. Unless you want to crawl down the rabbit hole that is singleton defunctionalization (the usual method for working with partially applied type functions), you'll want to avoid treating type functions as arguments. Use a plain data structure instead.
For example, if you write a type function that prefixes a type using a list of URL path segments:
type PrefixWith :: [Symbol] -> Type -> Type
type family PrefixWith segs t where
PrefixWith (seg:segs) t = seg :> PrefixWith segs t
PrefixWith '[] t = t
and then incorporate a call to this function within your API:
type MyAPI endpointTail result
= "blah"
:> Capture "a" A
:> PrefixWith endpointTail (Get '[JSON] result)
then this lets you write:
MyAPI ["hello","world"] HelloWorldT
Full code example:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
import Servant
import Data.Aeson
import Data.Kind
import GHC.Generics
import GHC.TypeLits
import Network.Wai.Handler.Warp
type PrefixWith :: [Symbol] -> Type -> Type
type family PrefixWith segs t where
PrefixWith (seg:segs) t = seg :> PrefixWith segs t
PrefixWith '[] t = t
type MyAPI endpointTail result
= "blah"
:> Capture "a" A
:> PrefixWith endpointTail (Get '[JSON] result)
data HelloWorldT = HelloWorld { success :: Int } deriving (Generic)
instance ToJSON HelloWorldT
type A = Int
main :: IO ()
main = run 8080 (serve (Proxy @(MyAPI ["hello", "world"] HelloWorldT))
(\n -> return (HelloWorld n)))
-- example URL, http://localhost:8080/blah/1/hello/world
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论