Haskell 如何让GHC将我的孤立实例“HasServer”和“HasClient”应用于“AuthProtect”?

Haskell 如何让GHC将我的孤立实例“HasServer”和“HasClient”应用于“AuthProtect”?,haskell,instance,servant,orphan,Haskell,Instance,Servant,Orphan,我使用来自的AuthProtectcombinator。那里没有太多的代码,实例HasServer(AuthProtect标记)在servant server中,而insanceHasClient(AuthProtect标记)在您使用的任何servant客户端中 我使用servant snap代替servant server和一个自定义HasClient实现obelisk项目,项目结构由三个阴谋包组成: 前端(由ghcjs编制) 通用(由ghcjs和ghc编制) 后端(由ghc编译) 我曾经

我使用来自的
AuthProtect
combinator。那里没有太多的代码,实例
HasServer(AuthProtect标记)
servant server
中,而insance
HasClient(AuthProtect标记)
在您使用的任何servant客户端中

我使用
servant snap
代替
servant server
和一个自定义
HasClient
实现
obelisk
项目,项目结构由三个阴谋包组成:

  • 前端(由ghcjs编制)
  • 通用(由ghcjs和ghc编制)
  • 后端(由ghc编译)
我曾经有一个自定义的
AuthProtect
实现,以及
common
包中的实例。但是,由于ghcjs,
common
既不能依赖于
servant snap
也不能依赖于
snap core

现在我将
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标记

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 ())

这也是我的假设。我已经导入了定义孤立实例的模块。也许我会尝试用一个最小的例子来说明这种行为。@ruben.moor在这样的问题中,一个最小的复制例子总是受欢迎的。这些最小的例子可以创造奇迹。我通过将
AuthProtect标记
替换为
AuthProtect“jwt”
解决了我的问题。如果我想保持它的通用性,我必须在实例约束中添加“KnownSymbol标记”。