我一直在尝试使用the Haskell graphql-api
library,它使用一些servant
样式类型技巧来表示类型级别的GraphQL API。它提供Handler
类型系列,用于提供GraphQL API的实际实现。我已经为测试目的创建了一个简单的API:
type Query = Object "Query" '[]
'[ Argument "id" Text :> Field "test" (Maybe Foo) ]
type Foo = Object "Foo" '[]
'[ Field "name" Text ]
我还有一个代表Foo
资源的Haskell类型,以及一个从数据库中检索它的函数:
data ServerFoo = ServerFoo
{ name :: Text
} deriving (Eq, Show)
lookupFoo :: Text -> IO (Maybe ServerFoo)
现在,我想为Handler
实施Query
。首先,我为Handler
实施了Foo
:
viewFoo :: ServerFoo -> Handler IO Foo
viewFoo ServerFoo { name } = pure $ pure name
然后我去实现了根处理程序,但是当我最终得到viewFoo
时,我意识到我不确定如何使用我的Maybe ServerFoo
函数。我试过这个:
handler :: Handler IO Query
handler = pure $ \fooId -> do
foo <- lookupFoo fooId
sequence $ fmap viewFoo foo
然而,这不起作用。它会产生以下类型错误:
• Couldn't match type ‘IO Text’
with ‘Object "Foo" '[] '[Field "name" Text]’
Expected type: ServerFoo
-> IO (Object "Foo" '[] '[Field "name" Text])
Actual type: ServerFoo -> Handler IO Foo
• In the first argument of ‘fmap’, namely ‘viewFoo’
In the second argument of ‘($)’, namely ‘fmap viewFoo foo’
In a stmt of a 'do' block: (sequence $ fmap viewFoo foo)
这对我来说似乎有些奇怪,因为我读过的例子似乎暗示Handler
被设计为成分。事实上,他们似乎是,因为做一些调整以删除Maybe
使得这个类型检查:
type Query = Object "Query" '[]
'[ Argument "id" Text :> Field "test" Foo ]
handler :: Handler IO Query
handler = pure $ \fooId -> do
Just foo <- lookupFoo fooId
viewFoo foo
然而,显然,这涉及到部分模式匹配,所以这不是一个好的解决方案。
我的结论是Handler IO
不是算子,或Handler m (Maybe a)
比我预期的更奇怪。不管是什么原因,这似乎是一个相当简单的用例,所以我想有必要有一些解决方案。