2016-07-17 2 views
0

이상한 오류가 발생하여 문제를 해결할 수 없습니다. 서번트를 사용하고 있으며 일반 인증 라이브러리 (예 : 백엔드가 없음)를 빌드하려고합니다.유형 인스턴스 및 유령 유형

type TokenProtect auth = AuthProtect "auth-token" 
type instance AuthServerData (TokenProtect auth) = Id auth 

Id 다른 유형의 가족 :

코드는 다음과 같다. 오류 메시지는 다음과 같습니다.

• Family instance purports to bind type variable ‘auth’ 
     but the real LHS (expanding synonyms) is: 
      AuthServerData (AuthProtect "auth-token") = ... 
    • In the type instance declaration for ‘AuthServerData’ 

이 코드를 어떻게 해결할 수 있는지 알고 계십니까?

+4

'TokenProtect'은 타입 동의어이며 오른쪽에'auth'를 사용하지 않습니다. 두 번째 줄에서 타입 동의어를 확장하면'AuthServerData (AuthProtect "auth-token") = Id auth' 형식이되므로 오른쪽의'auth'는 효과적으로 언 바인드됩니다. – kosmikus

+1

이 유형의 가족이 이루고자하는 목표와 관련하여 좀 더 폭 넓은 컨텍스트가 없어도이를 '수정'할 수있는 방법이 없습니다. 예 : 'AuthServerData'에 매개 변수를 추가하십시오 : type instance AuthServerData (AuthProtect "auth-token") auth = Id auth' – user2407038

+2

수정이'type' 대신에'newtype'을 사용하고 있다고 확신합니다. – Carl

답변

0

당신은 newtype 래퍼로 TokenProtect를 설정해야합니다 :

newtype TokenProtect auth = TokenProtect (AuthProtect "auth-token") 
type instance AuthServerData (TokenProtect auth) = Id auth 

그 이유는 해당 유형의 동의어 그냥있는 것입니다 : 동의어; 그래서 당신의 코드는 물론 auth 변수 언 바운드 유형을 의미

type instance AuthServerData (AuthProtect "auth-token") = Id auth 

를 작성하는 것과 동일합니다.

0

Servant를 사용하여 문제가 발생했으며 사용 사례가 원래의 질문자와 비슷하다고 생각됩니다.

type TokenProtect db = AuthProtect "auth-token" 
type instance AuthServerData (TokenProtect db) = DatabaseAuthResult db 

으로 : 기본적으로 나는 따라서 원래 포스터의 코드 같은 것을 필요로

class IsDatabase db where 
    type DatabaseAuthResult db :: * 
instance IsDatabase MyDBType 
    type DatabaseAuthResult MyDBType = DBUser 

처럼, 내 처리기를 통해 클래스가 제공하는 특정 유형의 동의어 제약 유형을 스레드 할 수 있도록 AuthProtect를 원 내가 해결할 수있는 한, Servant의 general auth implementation 구조 내에서는 불가능합니다. 선인장의 대답은 정확하게 newtype에서 실존 적을 포장해야한다고 말합니다. 그러나 그 자체로는 서번트 제약 조건과 관련된 컴파일 오류로 이어지기 때문에 HasServer 인스턴스와 관련된 문제 일 수 있습니다.

그러나 서번트의 AuthProtect, AuthHandler 등을 사용자 고유의 구현으로 복제하고이를 위해 자체 서버 버전을 작성하는 것이 일반적인 문제입니다.

-- import for all the internal servant stuff like addAuthCheck 
import Servant.Server.Internal.RoutingApplication 

data DBAuthProtect (tag :: k) db deriving (Typeable) 
newtype DBAuthHandler r db result = DBAuthHandler {unDBAuthHandler :: r -> Handler result} 

instance (HasServer api context 
     , HasContextEntry context (DBAuthHandler Request db (AuthServerData (DBAuthProtect tag db)))) 
    => HasServer (DBAuthProtect tag db :> api) context where 
    type ServerT (DBAuthProtect tag db :> api) m = AuthServerData (DBAuthProtect tag db) -> ServerT api m 
    route Proxy context subserver = 
    route (Proxy :: Proxy api) context (subserver `addAuthCheck` withRequest authCheck) 
     where 
     authHandler :: Request -> Handler (AuthServerData (DBAuthProtect tag db)) 
     authHandler = unDBAuthHandler (getContextEntry context) 
     authCheck :: Request -> DelayedIO (AuthServerData (DBAuthProtect tag db)) 
     authCheck = (>>= either delayedFailFatal return) . liftIO . runExceptT . authHandler 

그런 다음 AuthProtect 유사 이것을 사용할 수 있습니다, 그래서 뭔가 마지막

type TokenProtect db = DBAuthProtect "auth-token" db 
type instance AuthServerData (TokenProtect db) = DatabaseAuthResult db 
type ProtectedAPI db = "private" :> TokenProtect db :> Get [...] 
dbAuthHandler :: (IsDatabase db) => db -> DBAuthHandler Request db (DatabaseAuthResult db) 
dbAuthHandler db = DBAuthHandler $ \ req -> do 
    -- req :: Request 
    -- ... do some work here and return a type (DatabaseAuthResult db), so for MyDBType you would return DBUser - you have both the db itself and the request to work with 

처럼이 종의 serveWithContext를 사용하여 모두 함께이 문제를 놓고 상황에서 당신이 핸들러가 부분적으로

mkContext :: db -> Context '[DBAuthHandler Request db (AuthServerData db)] 
mkContext db = dbAuthHandler db :. EmptyContext 

main :: IO() 
main = do 
    db <- getMyDBSomehow -- a concrete type, say MyDBType 
    let myApi = (Proxy :: Proxy (ProtectedAPI MyDBType)) 
    serveWithContext myApi (mkContext db) handlers  
을 적용 제공

기본적으로이 방법은 다양한 변수와 비트를 통해 유형 변수를 스레드하여 api 매개 변수로 끝내는 것입니다 (핸들러와 마찬가지로) db 형식으로 처리되므로 API 유형 및 처리기에서 유형 동의어를 사용할 수 있습니다.

당신이 당신의 authHandler을 실행할 때 enter를 사용하여이 패턴 개선 (및 앱 모나드는 당신 serveWithContext에 전달 컨텍스트에 필요가있는 컨텍스트를 추가 할 수 있습니다 응용 프로그램에 대한 사용자 정의 모나드를 사용하는 경우, 그러나 그것은 넘어 이 질문의 범위 ...).

관련 문제