2012-05-17 4 views
2

을 나는 IO 모나드에서 실행되는 기능을 가지고 :컴파일 오류가 복잡한 오류 메시지

withDB :: (forall c. IConnection c => c -> IO b) -> IO b 
withDB fn = bracket (connectSqlite3 "int/db.sqlite3") disconnect fn 

을 그리고 지금은 일부 MonadIO m에서 실행을 일반화하기로 결정했다. 나는 방법 다음 그것을했다, 다시 발명으로 bracket을 내 scope (당신은 라이브러리의 일부를 알 수 있습니까?) :

Could not deduce (m ~ IO) 
from the context (MonadIO m) 
    bound by the type signature for 
    withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b 
    at src\... 
    'm' is a rigid type variable bound by 
    the signature for 
     withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b 
Expected type: IO b 
    Actual type: m b 
In the third argument of 'scope' namely 'fn' 
In the second argument of '($)', namely 
    'scope 
    (liftIO $ connectSqlite3 "int/db.sqlite3") 
    (\x -> liftIO $ disconnect x) 
    fn' 

그리고 지금 내 질문 :

나는 오류가 발생했습니다
scope :: MonadIO m => m a -> (a -> m b) -> (a -> m c) -> m c 
scope before after action = do 
    x <- before 
    r <- action x 
    _ <- after x 
    return r 

withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b 
withDB fn = liftIO $ scope 
         (liftIO $ connectSqlite3 "int/db.sqlite3") 
         (\x -> liftIO $ disconnect x) fn 

  1. m ~ IO는 무엇을 의미합니까? 처음 두 줄의 오류는 무엇이라고 말합니까? 또한,이 ~ haskell 코드에서 건설 보았다하지만 그것을 찾을 수 없습니다. 신장? 고정 유형 변수 란 무엇입니까?

  2. 오류가 발견되어 해결되었습니다. scope 전에 liftIO $을 제거하면 충분합니다. 하지만 그냥 재 시도주기였습니다. 이 오류 메시지의에 오류 위치에 대한 정보가 있습니까? 나는 'fn'에 뭔가 잘못된 것을 본다. 좋아, 나는 그것에 대해 조금 생각하고 추측했다 : GHC는 위에서 아래로 타입을 추론한다. 그리고 liftIOmIO이어야하지만 fn은 일반적인 유형이 m이므로 오류입니다. haskell 컴파일러는 위에서 아래로 유추합니까? 그리고 (더 중요한) GHC가 산출물의 하위 표현에 대해 유추하는 유형을 볼 수 있습니까?

이 긴 질문을 읽어 주셔서 감사합니다.

+1

'scope' 함수는'bracket'과 정확히 똑같지 않습니다. 'bracket '은 계산을 자원 할당/해제의'괄호 '로 래핑 할뿐만 아니라 예외를 처리하여 예외가 발생하더라도 리소스를 해제합니다. 'scope' 함수는 리소스를 해제하지 않고 종료합니다. 어쩌면이 경우에는 중요하지 않지만 여전히 그렇습니다. –

+0

일반 MonadIO가 예외를 처리 할 수 ​​있습니까? – demi

+0

나는 이것을'liftIO'로 관리 할 수 ​​있다고 생각합니다. 결국'IO a -> m a' 타입을 가지며 모든 예외 처리 루틴은'IO' 타입을가집니다.예외 관련 함수는 다음에서 찾을 수 있습니다. http://hackage.haskell.org/packages/archive/base/4.5.0.0/doc/html/Control-Exception.html –

답변

4

liftIO :: (MonadIO m) => IO a -> m aliftIO $ scope ... 말하여, 그래서 당신이 scope ...이 유형 IO b이 있어야 말을하는지, IO 조치를 취합니다. 즉, scope에 대한 인수는 IO 모나드를 사용해야합니다. scope의 사용이 mIO을해야 보장하기 때문에, 당신은 상황에서 이러한 유형의 것으로 scope 생각할 수 있습니다 :이 때문에

scope :: IO a -> (a -> IO b) -> (a -> IO c) -> IO b 

liftIO 내부 scope 호출 할 아무것도; 그들은 단순히 IO a에서 IO a으로 변환 중이고 이 아닌 m에서 작동하므로 fn을 사용할 수 없습니다. liftIO은 (fn이 실행 중이므로 IO이 아니기 때문에)에서 실행하지 않고 을 직접 실행하고 m으로 실행했기 때문에 해결되었습니다.

+0

좋아요, 그래서 어떤 타입 추론이 가장 바깥 쪽 표현에서 내부 (즉, 표현 트리의 루트에서 리프까지, 리프에서 루트로가 아닌)로 간다? 그리고 I ~ IO는 무엇을 의미합니까? – demi

+4

'm ~ IO '는'm '이'IO '와 동일하다는 것을 증명하려고하지만, 반드시 (꼭) 필요하지는 않습니다. –

+0

@demi : 어떤 방향 추론이 작동하는지에 관한 것은 아니지만, 제약 조건에 관해서 :'liftIO' *를 사용하려면'scope ... '가'IO b' 타입을 필요로합니다. Louis가 말했듯이, "(a ~ b)'를 추론 할 수 없다는 것은 당신의 코드가'a'와'b'가 동일하게 작동하도록 요구한다는 것을 의미하지만 반드시 그런 것은 아닙니다. – ehird