2

저는 현재 haskell에 서버를 만들고 언어에 대한 초보자로서 새로운 접근법을 시도하고 싶습니다. 아이디어는 기본 모나드에 대해 알지 못하는 채로 작동하는하스켈에서 타입 시스템과 싸우지 않고 모나드를 추상화하는 방법은 무엇입니까?

isGetRequest :: (SupportsRequests m r) => m Bool 
    isGetRequest = do 
     method <- liftRequests $ requestMethod 
     return $ method == GET 

    class (Monad m, RequestSupport r) => SupportsRequests m r | m -> r where 
     liftRequests :: r a -> m a 

    class (Monad r) => RequestSupport r where 
     requestMethod :: r Method 

과 같은 라이브러리 메소드를 작성할 수 있다는 것입니다. 물론이 예제에서는 (RequestSupport r) 모나드에서 isGetRequest를 직접 작동시키는 것으로 충분했지만 내 라이브러리가 모나드에 둘 이상의 제약 조건을 가질 수 있다는 아이디어가 있습니다. 그러나 나는 같은 모듈에있는 모든 다른 관심사를 구현하고 다른 모듈 (고아 인스턴스!)에 퍼트 리는 것을 원하지 않습니다. 이것이 m 모나드가 Supports* 클래스만을 구현하여 실제 문제를 다른 모나드에 위임 한 이유입니다.

위의 코드는 완벽하게 작동합니다 (GHC의 일부 언어 확장 포함). 아니 내가 얻을 오류

class (Monad m, CRUDSupport c a) => SupportsCRUD m c a | m a -> c where 
    liftCRUD :: c x -> m x 

class (Monad c) => CRUDSupport c a | c -> a where 
    list :: c [a] -- List all entities of type a 

: 불행하게도, 나는 CRUD 몇 가지 문제를 가지고 문제를 (읽기 업데이트 삭제 만들기 없음)

Could not deduce (SupportsCRUD m c a0) from the context [...] 
The type variable 'a0' is ambiguous [...] 
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes 
When checking the class method: liftCRUD [...] 

이 유형 검사처럼 보인다

좋아하지 않습니다 그 a 매개 변수 리프트 CRUD의 서명에서 직접 발생하지 않습니다. a은 기능 종속성에서 파생 될 수 없으므로 이해할 수 있습니다.

내 뇌의 유형 검사기는 나중에 CRUD에 관한 일부 메소드가 라이브러리 메소드에서 실행될 때 AllowAmbiguousTypes를 사용하여 a 유형을 유추하는 데 문제가 없어야한다고 알려줍니다. 불행하게도, GHC 예를 들어,이 추론 단계를 할 수없는 것 같다

bookAvailable :: (SupportsCRUD m c Book) => m Bool 
bookAvailable = do 
    books <- liftCRUD (list :: c [Book]) -- I use ScopedTypeVariables 
    case books of 
     [] -> return False 
     _ -> return True 

아직도 컴파일러를 추론 할 수없는 나는 것 같다

Could not deduce (SupportsCRUD m c0 a1) arising from a use of 'liftCRUD' [...] 
The type variables c0, a1 are ambiguous [...] 

를 얻을 수 있습니다. 이 문제를 해결할 방법이 있습니까? 아니면 적어도 컴파일러가 무엇을 추론 할 수 있는지 이해하는 방법?

최고 감사합니다, bloxx

+3

'SupportsCRUD' 클래스 선언은 'm','c' 및'a'의 세 가지 유형 변수를 포함합니다. 귀하의 예제에서이 세 가지 변수에 대해 컴파일러가 예상하는 값이 무엇이고 그 이유는 무엇이라고 말할 수 있습니까? –

+1

'm a -> c'라는 펀드가 의도 한 바는 무엇입니까? 이것이 의미하는 바는 "'c '의 선택은 타입 검사기가'm'과'a'만으로 인스턴스를 선택할 수 있다는 특수한 타입 인'm'과'a'에 의해 유일하게 결정됩니다. 상황에 따라 달라지며 'c'는 인스턴스 헤드에 넣은 것과 같습니다. – jberryman

+0

@jberryman 실제로 컴파일러에게 모나드와 "CRUD 엔티티"a에 대해 유형 검사기가 고유 한 모나드 C (CRUD 연산을 지원하는 하나의 고유 한 모나드)를 선택할 수 있도록 알려줍니다. – bloxx

답변

3

당신은 또한 당신이 forall과 범위에 있어야 할 변수를 바인드해야 ScopedTypeVariables 사용하십시오. 그래서해야 필요했다 모든했다

bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool 
... 

나 코드를 컴파일 갈 수있는 위해 (내가 질문을 입력에서 오타했다 생각하는 만든 몇 가지 사소한 수정 후).

+0

와우! 음, 그렇게 놀랍습니다. 이제는 제가 의문입니다. 왜 GHC는 'liftCRUD'유형을 추론 할 때 'a ~ Book'을 선택할 의향이 있습니까? 분명히 그 선택에 따라 적절한 인스턴스를 사용할 수 있습니다. 하지만 분명히 나중에 나와서 더 좋은 선택이 될 수있는 또 다른 인스턴스를 추가 할 수 있습니다.이 인스턴스는 나에게 "애매한"소리를 지르는 것입니다. –

+2

아하 (Aha), 그리고 "분명히 한 사람이 나중에 다른 인스턴스를 추가 할 수있다"는 나의 주장에 대한 증인을 만들기 위해 노력한 결과 아마도 그 이유를 알 수있을 것이라고 생각합니다 : CRUDSupport에 핵심적인 'c -> a'펀드가 있습니다. , 모든'SupportsCRUD' 인스턴스는 상응하는'CRUDSupport' 인스턴스를 의미합니다. –

+0

@DanielWagner 예, 그것은 너무 나를 트립했다. 나는 CRUDSupport가 이후에 선언 되었기 때문에 SupportsCRUD의 수퍼 클래스 였고, 나는 슈퍼 클래스가 먼저 온다는 것에 익숙하지 않았다. – luqui