2013-02-14 10 views
4

갖는 서로 다른 EitherT와내가이 일을 어떻게 ... ... 기본 모나드 예를 들어

consumer :: Proxy p =>() -> Consumer p a (EitherT String IO)() 
producer :: Proxy p =>() -> Producer p a (EitherT ByteString IO) r 

를 프록시를 결합?

session :: EitherT ByteString (EitherT String IO)() 
session = runProxy $ producer >-> consumer 

는 : 나는 Control.Proxy.Tutorial에서 Mixing Base Monads을 읽었습니다. 첫 번째 예제를 얻었지만 인위적인 예제를 이해할 수는 없습니다. 모호하지는 않지만 그렇게 고안되지 않은 예제가 더 있으면 hoist 및 을 사용하여 기본 모나드 조합을 일치시키는 방법을 명확히 할 수 있습니다.

+1

타입 추론기를 사용하여 'runProxy $ hoist lift'를 입력하여'session'을 확인했습니다. 생산자> -> 호이스트 (호이스트 리프트). 소비자 '. 나는 또한'EitherT e'를위한'MFunctor' 인스턴스를 만들어야했습니다 :'instance (EitherT e) where hoist nat = mapEitherT nat'. 나는 왜 이것이 효과가 있는지 아직 알지 못하기 때문에 질문을 끝내지 않을 것입니다. –

답변

8

MT1 MT2 MT3 M a과 같은 모나드 변압기 스택이 있다고 가정하면 M은 기본 모나드입니다.

lift을 사용하면 왼쪽에 새로운 모나드 변환기를 추가 할 수 있습니다. 변압기가 될 수 있으므로 ?으로 상징화하겠습니다.

lift :: MT1 MT2 MT3 M a -> ? MT1 MT2 MT3 M a

hoist 사용하면 왼쪽 요소의 오른쪽에있는 모나드 스택을 조작 할 수 있습니다. 그것을 조작하는 방법? lift을 공급하여 예를 들어 :

hoist lift :: MT1 MT2 MT3 M a -> MT1 ? MT2 MT3 M a

hoistlift의 조합을 사용하여 모나드 변압기 스택의 모든 지점에서이 "와일드 카드"를 삽입 할 수 있습니다.

hoist (hoist (hoist lift)) :: MT1 MT2 MT3 M a -> MT1 MT2 MT3 ? M a

이 기술

hoist (hoist lift) :: MT1 MT2 MT3 M a -> MT1 MT2 ? MT3 M a

는 귀하의 예제에서 두 개의 모나드 스택을 동일하게 할 수 있습니다.

+0

정확히 '호이스트'와 '리프트'사용에 대해 제가 놓친 직관적 인 설명입니다. –

5

패키지 EitherT 유형이 나오는 패키지는 첫 번째 인수를 수정하는 몇 가지 기능을 제공합니다 (예 : EitherT).

bimapEitherT :: Functor m => (e -> f) -> (a -> b) -> EitherT e m a -> EitherT f m b 

당신은 EitherT ByteString IO a (또는 그 반대) 내로 EitherT String IO a 좌회전 일부 적절한 부호화 (복호화)과 함께, 이것을 사용 후 hoistConsumer 또는 Producer 모나드 변압기로 그 변화.

+0

사실 저는 다른 유형의 예로'String'과'ByteString'을 사용하고있었습니다. 실제로 나는 예외를 다루고있다. 나는'toException'을 사용하여 모든 예외를'SomeException'으로 바꿀 수 있다고 생각하지만 여전히 예외가 오는 프록시를 구별하고 싶습니다. –

5

실제로 두 가지 해결책이 있습니다.

첫 번째 해결 방법은 Daniel Wagner가 제안한 것입니다. 두 개의 기본 모나드를 수정하여 동일한 Left 유형을 사용합니다. 예를 들어, 둘 다 ByteString으로 정규화 할 수 있습니다.이렇게하려면, 우리가 처음 ByteStringpack 기능을 가지고 :

다음
pack :: String -> ByteString 

우리가이 EitherT의 왼쪽 값에 일을 들어 올려 것은 :

import Control.Error (fmapLT) -- from the 'errors' package 

fmapLT pack :: (Monad m) => EitherT String m r -> EitherT ByteString m r 

이제 우리는 필요로하는 변환을하는 대상에 hoist를 사용 Consumer의 기본 모나드 :

hoist (fmapLT pack) 
:: (Monad m, Proxy p) 
=> Consumer p a (EitherT String m) r -> Consumer p a (EitherT ByteString m) r 

지금 당신은 당신의 소비자가 무서운 구성 할 수 그들은 같은 기본 모나드를 가지고 있기 때문에 당신의 프로듀서와 함께 ctly.

두 번째 해결책은 Daniel Diaz Carrete가 제안한 해결책입니다. 대신 두 개의 파이프를 EitherT 레이어를 모두 포함하는 공통 모나 드 변압기 스택에 동의하게 만듭니다. 두 레이어를 중첩 할 순서를 결정하기 만하면됩니다.

EitherT ByteString 변압기 외부에 EitherT String 변압기를 겹쳐 놓았다고 가정 해 봅시다. 그게 당신의 최종 목표 모나드 변압기 스택이 될 것이라고 의미 :

(Proxy p) => Session (EitherT String (EitherT ByteString p)) IO r 

지금 당신은 변압기 스택을 대상으로하여 파이프의 양쪽을 촉진 할 필요가있다. 당신의 Consumer를 들어

, 당신은 당신이 마지막 모나드 변압기 스택과 일치 할 경우 EitherT String 사이 IOEitherT ByteString 레이어를 삽입해야합니다. 레이어를 만드는 것은 쉽습니다. 을 사용하면되지만 프록시 모나드 변환기와 EitherT String 모나드 변압기를 건너 뛸 필요가 있으므로 hoist을 두 번 사용하도록 두 특정 레이어 사이를 들어야합니다.

당신의 Producer를 들어
hoist (hoist lift) . consumer 
:: Proxy p =>() -> Consumer p a (EitherT String (EitherT ByteString IO))() 

, 당신은 당신이 마지막 모나드 변압기 스택과 일치 할 경우 프록시 모나드 변압기와 EitherT ByteString 변압기 사이에 EitherT String 레이어를 삽입해야합니다. 다시 말하면 레이어를 만드는 것이 쉽습니다. lift을 사용하기 만하지만 두 개의 특정 레이어 사이에서 그 리프트를 타겟팅해야합니다. 당신은 단지 hoist,하지만 당신은 단지 올바른 자리에 잠시 멈춰서는 lift에 프록시 모나드 변압기 건너 뛸 필요가 있기 때문에이 시간 당신은 한 번만 사용 :

hoist lift . producer 
:: Proxy p =>() -> Producer p a (EitherT String (EitherT ByteString IO)) r 

지금 당신의 생산자와 소비자가 같은 모나드 변압기가 스택하고 직접 작성할 수 있습니다.

이제 0120-lift이 프로세스가 "올바른 작업"을 수행하고 있는지 궁금 할 수 있습니다. 대답은 '예'입니다. 카테고리 이론의 마법의 일부는 lift을 사용하여 "빈 모나드 변압기 레이어"를 정확히 삽입한다는 의미를 정확히 정의 할 수 있으며, 마찬가지로 hoist을 사용하여 "두 모나드 변압기 사이에서 무언가를 목표로 삼는다"는 의미를 엄격하게 정의 할 수 있습니다 이론적으로 영향을받은 몇 가지 법칙을 명시하고 lifthoist이 해당 법을 준수하는지 확인합니다.

우리가 이러한 법칙을 만족하면 정확히 lifthoist이하는 모든 핵심적인 세부 사항을 무시할 수 있습니다.범주 이론은 모나드 변압기와 공간 코드 사이에 공간적으로 "리프트를 삽입하는"면에서 우리가 생각하는 매우 높은 추상화 수준에서 작업 할 수있게 해줌으로써 우리의 공간적 직감을 마술처럼 정확하게 해석합니다.

내 생각에 첫 번째 해결 방법은 단일 EitherT 레이어에서 생산자와 소비자간에 오류 처리 기능을 공유 할 수 있기를 원할 것입니다.

+0

네가 틀린 다니엘을 가지고 있다고 생각해. 공평히 지켜야 할 것이 너무 많아. ;] –

+0

맞음! 얼마나 창피한가. :) 나는 그것을 고칠 것이다. –

+0

위대한 대답, 나는 이것을 받아 들일 수 있다고 표시 할 수있었습니다. 나는 정말로하려고했던 것을 더 닮기 위해'String'과'ByteString' 대신에 예외를 두어야했습니다. 첫 번째 접근법은'pack' 대신에'toException'을 사용할 것입니다, 그러나 여전히 어떤 예외가 다른 예외를 더 쉽게 처리 할 수 ​​있는지 확실하지 않습니다. –