如何让 GHC 为 `AuthProtect` 应用我的孤立实例 `HasServer` 和 `HasClient`?
How to get GHC to apply my orphaned instances `HasServer` and `HasClient` for `AuthProtect`?
我使用 servant: Servant.API.Experimental.Auth 中的 AuthProtect
组合器。那里没有太多代码,实例 HasServer (AuthProtect tag)
在 servant-server
中,实例 HasClient (AuthProtect tag)
在您使用的任何仆人客户端中。
我使用 servant-snap
而不是 servant-server
和一个 obelisk
项目的自定义 HasClient
实现,项目结构由三个 cabal 包组成:
- 前端(由ghcjs编译)
- common(由ghcjsandghc编译)
- 后端(由ghc编译)
我曾经有一个 AuthProtect
的自定义实现以及 common
包中的实例。但是,common
既不能依赖于 servant-snap
也不能依赖于 snap-core
因为 ghcjs.
现在我将 HasServer
实例移动到后端......没问题,对吧?错误的。一旦 HasServer
实例被孤立,ghc 就不再正确解析我的 api 类型。就好像孤儿实例根本不存在一样。
这是为什么?
有什么我能做的?
实例是全局的……理论上。实际上,为了支持单独编译,它们通过导入传播。因此,从使用它的模块中导入定义实例的模块。传递地导入它就足够了——即导入一个模块,该模块导入一个模块,该模块导入定义实例的模块。
其中一个解决了我的问题:
instance HasServer api context m => HasServer (AuthProtect "jwt" :> api) context m where
type ServerT (AuthProtect "jwt" :> api) context m =
String -> ServerT api context m
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
route (Proxy :: Proxy (AuthProtect "jwt" :> api)) context subserver =
route (Proxy :: Proxy api) context (subserver `addAuthCheck` withRequest authCheck)
where
authCheck :: Request -> DelayedM m String
authCheck =
liftIO . evalSnap (pure "account info")
(\x -> pure $! (x `seq` ()))
(\f -> let !_ = f 0 in pure ())
如果出于某种原因我不想专攻 AuthProtect "jwt"
,我必须提供约束条件 KnownSymbol tag
。
instance (KnownSymbol tag, HasServer api context m) => HasServer (AuthProtect tag :> api) context m where
type ServerT (AuthProtect tag :> api) context m =
String -> ServerT api context m
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
route (Proxy :: Proxy (AuthProtect tag :> api)) context subserver =
route (Proxy :: Proxy api) context (subserver `addAuthCheck` withRequest authCheck)
where
authCheck :: Request -> DelayedM m String
authCheck =
liftIO . evalSnap (pure "account info")
(\x -> pure $! (x `seq` ()))
(\f -> let !_ = f 0 in pure ())
我使用 servant: Servant.API.Experimental.Auth 中的 AuthProtect
组合器。那里没有太多代码,实例 HasServer (AuthProtect tag)
在 servant-server
中,实例 HasClient (AuthProtect tag)
在您使用的任何仆人客户端中。
我使用 servant-snap
而不是 servant-server
和一个 obelisk
项目的自定义 HasClient
实现,项目结构由三个 cabal 包组成:
- 前端(由ghcjs编译)
- common(由ghcjsandghc编译)
- 后端(由ghc编译)
我曾经有一个 AuthProtect
的自定义实现以及 common
包中的实例。但是,common
既不能依赖于 servant-snap
也不能依赖于 snap-core
因为 ghcjs.
现在我将 HasServer
实例移动到后端......没问题,对吧?错误的。一旦 HasServer
实例被孤立,ghc 就不再正确解析我的 api 类型。就好像孤儿实例根本不存在一样。
这是为什么?
有什么我能做的?
实例是全局的……理论上。实际上,为了支持单独编译,它们通过导入传播。因此,从使用它的模块中导入定义实例的模块。传递地导入它就足够了——即导入一个模块,该模块导入一个模块,该模块导入定义实例的模块。
其中一个解决了我的问题:
instance HasServer api context m => HasServer (AuthProtect "jwt" :> api) context m where
type ServerT (AuthProtect "jwt" :> api) context m =
String -> ServerT api context m
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
route (Proxy :: Proxy (AuthProtect "jwt" :> api)) context subserver =
route (Proxy :: Proxy api) context (subserver `addAuthCheck` withRequest authCheck)
where
authCheck :: Request -> DelayedM m String
authCheck =
liftIO . evalSnap (pure "account info")
(\x -> pure $! (x `seq` ()))
(\f -> let !_ = f 0 in pure ())
如果出于某种原因我不想专攻 AuthProtect "jwt"
,我必须提供约束条件 KnownSymbol tag
。
instance (KnownSymbol tag, HasServer api context m) => HasServer (AuthProtect tag :> api) context m where
type ServerT (AuthProtect tag :> api) context m =
String -> ServerT api context m
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt . s
route (Proxy :: Proxy (AuthProtect tag :> api)) context subserver =
route (Proxy :: Proxy api) context (subserver `addAuthCheck` withRequest authCheck)
where
authCheck :: Request -> DelayedM m String
authCheck =
liftIO . evalSnap (pure "account info")
(\x -> pure $! (x `seq` ()))
(\f -> let !_ = f 0 in pure ())