2011-09-14 3 views
5

죄송합니다. 질문이 조금 사소한 것처럼 보일 경우 ... 그것은 나를위한 것이 아닙니다.변압기로 모나드 조성을 추상화

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

물론입니다, 잘 행동 모나드 : 나는 행복하게 다음 모나드를 구성했다. ReaderT는 모나드 변환기이고 State는 상태 모나드이며, AlgRO와 AlgState는 각각 i가 변경 가능하고 읽기 전용 상태로 매개 변수화 된 데이터 유형입니다. 나는이 같은 깔끔한 모나드 newtype은와 변압기, 뭔가 그 있도록하려면 지금 :

newtype SbT m i a = SbT { 
    runSbT:: m (SB i a) 
} 

어떻게 진행해야합니까? 나는 (Monad typeclass의) bind 메소드를 모으기조차 할 수 없다. (MonadTrans의) "lift"가 훨씬 적다 ... 자동 유도가 도움이 될 것 같지만,이 경우 어떻게 작동하는지 이해하고 싶다.

미리 감사드립니다.

답변

10

나는 SbT에 대한 정의가 원하는 것이라고 생각하지 않습니다. 즉, 펑터 구성을으로 정의하고 m 매개 변수가 Functor 또는 Applicative이라고 가정하면이 속성을 유지해야합니다. 그러나 그러한 구성은 일반적으로 두 개의 다른 모나드 중 하나에서 새로운 모나드를 생성하지 않습니다. 해당 주제에 대한 자세한 내용은 this question을 참조하십시오.

그래서 으로 만들면 원하는 모나드 변환기를 만들 수 있습니까? 모나드는 직접 작성하지 않지만 모나드 변압기을 구성 할 수 있습니다. 따라서 기존 트랜스포머를 이용해 새로운 트랜스포머를 제작하려면 본질적으로 컴포지션에 이름을 지정하기 만하면됩니다. 이것은 newtype과 다릅니다. 을 트랜스포머 스택에 전달하는 대신 직접 적용하기 때문입니다.

모나드 변압기를 정의하는 데있어 유의해야 할 점 중 하나는 모나드 변압기를 반드시 특정 방식으로 "거꾸로"작동한다는 것입니다. 모나드에 복합 변압기를 적용하면 "가장 안쪽에있는"변압기가 첫 번째 균열을 얻습니다. 생성 된 변환 된 모나드는 다음 변환기가 작동 할 것입니다. & c. 이것은 구성된 함수를 인수에 적용 할 때 얻는 순서와 다르지 않습니다. (f . g . h) xh의 인수를 먼저 제공합니다. f이 컴포지션의 "첫 번째"기능 인 ​​경우에도 마찬가지입니다.

좋아, 그래서 귀하의 복합 변압기는에 적용된 모나드를 가지고 음 .... 죄송합니다, SB는 모나드에 적용 이미 것으로 밝혀이며, 가장 안쪽 변압기에 전달해야합니다. 이것이 작동하지 않는 것은 놀라운 일이 아닙니다. 먼저 그것을 제거해야합니다. 어디 있니? 아니 State - 우리는 수 있지만 우리가하고 싶지 않아, 왜냐하면 당신이 원하는 부분입니다. 음,하지만 기다려 - State은 무엇으로 정의 되었습니까? 오 그래 :

type State s = StateT s Identity 

아하, 우리가 간다. 거기에 Identity을 가져 가자.

type SB' i m a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
type SB i a = SB' i Identity a 

하지만 지금 SB'는 모나드 변압기처럼 의심 같습니다

type SB i a = ReaderT (AlgRO i) (StateT (AlgState i) Identity) a 

그런 다음 우리가 게으른 엉덩이를 걷어차 : 동등한 형성

type SB i a = ReaderT (AlgRO i) (State (AlgState i)) a 

: 우리는 현재의 정의에서 이동 정의와 좋은 이유가있다. 그래서 우리는 newtype 래퍼를 재 작성하고 거기에 몇 가지 인스턴스를 던져 : 메모를 취할

newtype SbT i m a = SbT { getSB :: ReaderT (AlgRO i) (StateT (AlgState i) m) a } 

instance (Functor m) => Functor (SbT i m) where 
    fmap f (SbT sb) = SbT (fmap f sb) 

instance (Monad m) => Monad (SbT i m) where 
    return x = SbT (return x) 
    SbT m >>= k = SbT (m >>= (getSB . k)) 

instance MonadTrans (SbT i) where 
    lift = SbT . lift . lift 

runSbT :: SbT i m a -> AlgRO i -> AlgState i -> m (a, AlgState t) 
runSbT (SbT m) e s = runStateT (runReaderT m e) s 

몇 가지 : 여기 runSbT 기능은 오히려 각각에 대해 구성된 "실행"기능 필드 접근이 아니라 우리가 알고있는 스택의 변압기. 마찬가지로 lift 함수는 두 개의 내부 변압기에 대해 한 번 들어 올린 다음 최종 newtype 래퍼를 추가해야합니다. 이 두 가지 모두 단일 모나드 변압기로 작동하며 실제로는 합성물이라는 사실을 숨 깁니다.

원하는 경우, 구성된 변환기에 대한 인스턴스를 해제하여 MonadReaderMonadState에 대한 인스턴스를 작성하는 것이 간단해야합니다.

+0

이렇게하면됩니다. 감사! – dsign

2

을 newtype의 주변에 추가로 포장 하시겠습니까? 나는 다음을 제안 : 쓰기에 instance Monad (Sb i) 조금 쉽게해야

newtype Sb i a = Sb { runSb :: SB i a } 

.... 만약 당신이 정말로 모나드 변압기를 쓰려고한다면, 변압기를 항상 내려야합니다. 예를 들어,

type SBT m i a = ReaderT (AlgRO i) (StateT (AlgState i) m) a 
newtype SbT m i a = SbT { runSbT :: SBT m i a } 

관심의 두 번째 점으로, 그것은 (그들이 항상 "완벽하게 적용"해야하기 때문에) type 동의어를 η-감소하는 것이 바람직입니다; SBSBT과 같이하면 다음과 같이 표시됩니다.

type SB i = ReaderT (AlgRO i) (State (AlgState i)) 
type SBT m i = ReaderT (AlgRO i) (StateT (AlgState i) m) 
+0

그건 그렇고, 당신은 그런 식으로 'SbT'밖으로 모나드 변압기를 만들 수 없습니다. 변압기는 종류가 (* -> *) -> * -> *', 즉 모나드와 유형을 인수로 취합니다. 'i' 매개 변수가 먼저 있어야하므로 부분적으로 적용 할 수 있습니다. –