如何解析此GHC类型检查错误消息?

时间:2018-08-23 06:05:45

标签: haskell servant

我被这个GHC(版本8.4.3)类型检查错误所困扰。这是我正在研究的Haskell Servant代码库的摘录。如果有人可以解释此消息的原因,我将不胜感激。对于代码的长度,我们深表歉意,但是我无法进一步减少它。

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}

module Api2 where

import Control.Lens (preview)
import Control.Lens.Cons
import Control.Monad.Error.Class
import Control.Monad.Except (runExceptT)
import Control.Monad.IO.Class (liftIO, MonadIO)
import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
import Crypto.JOSE.Compact
import Crypto.JOSE.Error
import Crypto.JWT (JWT, JWK, JWTError, verifyClaims, decodeCompact,
               defaultJWTValidationSettings, stringOrUri, ClaimsSet, 
               AsJWTError)
import Data.Aeson (decode, encode)
import Data.Aeson.Types
import Data.Maybe (fromJust)
import Data.Text as T
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Word8(isSpace)
import Network.Wai (Request, requestHeaders)
import Servant.API.Experimental.Auth (AuthProtect)
import Servant.Auth.Server (FromJWT, ToJWT)
import Servant.Server.Experimental.Auth (AuthHandler, AuthServerData, 
         mkAuthHandler)
import Servant.Server.Internal.ServantErr
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as L

data User = User

authHandler :: AuthHandler Request User
authHandler = mkAuthHandler authF2

authF2 :: (MonadIO m,
       MonadError ServantErr m) =>
      Request -> m User
authF2 req = do
  case lookup "Authorization" (requestHeaders req) of
    Nothing -> throwError err401
    Just authH -> do
      let (b, rest) = BS.break isSpace authH

    if T.toLower (decodeUtf8 b) == "bearer"
      then do
        claims <- liftIO $ verifyJwt "" (L.fromStrict rest) 
                   ("audience"::String)
        return $ User
      else throwError err401

verifyJwt
  :: forall e s. (Crypto.JWT.AsJWTError JWTError, (AsError e), 
                 AsJWTError e,
              Control.Lens.Cons.Cons s s Char Char, Monoid s) =>
     FilePath
     -> L.ByteString
     -> s
     -> IO (Either e Crypto.JWT.ClaimsSet)
verifyJwt jwkFilename jwtData aud = do
  let
    aud' = fromJust $ preview stringOrUri aud
    conf = defaultJWTValidationSettings (== aud')

  Just k <-  decode <$> L.readFile jwkFilename
  result <- runExceptT
    (decodeCompact jwtData >>= verifyClaims conf (k :: JWK))
  return result

在GHC中加载此文件时,出现以下错误。

• Could not deduce (AsError e0) arising from a use of ‘verifyJwt’
  from the context: (MonadIO m, MonadError ServantErr m)
    bound by the type signature for:
               authF2 :: forall (m :: * -> *).
                         (MonadIO m, MonadError ServantErr m) =>
                         Request -> m User
    at servant-api-server/src/Api2.hs:(37,1)-(39,27)
  The type variable ‘e0’ is ambiguous
  These potential instances exist:
    instance AsError Crypto.JOSE.Error.Error
      -- Defined in ‘Crypto.JOSE.Error’
    instance AsError JWTError -- Defined in ‘Crypto.JWT’
• In the second argument of ‘($)’, namely
    ‘verifyJwt "" (L.fromStrict rest) ("audience" :: String)’
  In a stmt of a 'do' block:
    claims <- liftIO
                $ verifyJwt "" (L.fromStrict rest) ("audience" :: String)
  In the expression:
    do claims <- liftIO
                   $ verifyJwt "" (L.fromStrict rest) ("audience" :: String)
       return $ User
   |
48 |           claims <- liftIO $ verifyJwt "" (L.fromStrict rest) ("audience"::String)

|                              
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1 个答案:

答案 0 :(得分:2)

调用verifyJwt :: forall e s. ...时,GHC指出您想将s类型的变量实例化为String,但无法确定要实例化{{1} }输入变量。

尽管您必须启用e扩展名,但可以通过为claims提供显式类型签名来解决此问题。您可以选择想要的instance of AsError

例如,选择ScopedTypeVariables

e ~ Error

与类型错误无关,但是您是否还不想在返回authF2 :: (MonadIO m, MonadError ServantErr m) => Request -> m User authF2 req = do case lookup "Authorization" (requestHeaders req) of Nothing -> throwError err401 Just authH -> do let (b, rest) = BS.break isSpace authH if T.toLower (decodeUtf8 b) == "bearer" then do (claims :: Either Error Crypto.JWT.ClaimsSet) <- liftIO $ verifyJwt "" (L.fromStrict rest) ("audience"::String) return $ User else throwError err401 之前检查claims不是Left吗?