如何让 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 包组成:

我曾经有一个 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 ())