2011-07-06 1 views
5

문제는 이것입니다. 나는 가지고있다 :mapMonadTrans :: MonadTrans xT => (m a -> n b) -> xT m a -> xT n b

f :: MonadIO m => ReaderT FooBar m Answer; 
f = (liftIO getArgs) >>= ... 

나는 이것을 수정 된 인수와 함께 실행해야한다. m를 알 수 있기 때문에 나는 모든 m에 대한 m로 (withArgs의 인수를) 변환 어떻게 든 필요가 있기 때문에, 단순히

mapReaderT (withArgs args) :: ReaderT r IO b -> ReaderT r IO b 

을 사용할 수 없습니다. 내가 찾은

하나의 가능성은, 따라서, 내 자신의 withArgs을 정의하는 것입니다 : 그러나

import System.Environment (setArgs, freeArgv); 
withArgv new_args act = do { 
    pName <- liftIO System.Environment.getProgName; 
    existing_args <- liftIO System.Environment.getArgs; 
    bracket (liftIO $ setArgs new_args) 
      (\argv -> do { 
         _ <- liftIO $ setArgs (pName:existing_args); 
         liftIO $ freeArgv argv; 
        }) 
      (const act); 
}; 

withArgs xs act = do { 
    p <- liftIO System.Environment.getProgName; 
    withArgv (p:xs) act; 
}; 

, 이것은 kludge이며, 하나의 함수에 특정 - 내가해야 모든 withX :: X -> IO a -> IO a를 다시 쓰기, 예를 들어, Control.Exception.handle

더 좋은 방법은 무엇입니까?

편집 : 핸들의 경우 Control.Monad.CatchIO를 발견했습니다. 다른 경우에는 위의 kludge를 피하기 위해 또 다른, 간결한 kludge (게시 가치가없는)를 사용했습니다. 아직도 더 나은 해결책을 찾고 있습니다!

+0

'f '에서 타입 시그니처를 제거하면 어떨까요? 'MonadIO'에 대한 제약이 너무 제한적인지 궁금합니다. –

+0

'f'에서 입출력을해야합니다. 그렇지 않으면 엄청납니다. (실제로, 타입 b의 값을 얻기위한 함수가있는 데이터 타입 a가 있고, 함수는 a 타입의 일부 값이 I/O를 수행하여 b를 생성 할 수 있도록 충분히 일반적이어야합니다.) –

+0

@strake : Control.Monad.CatchIO에 문제가 있음을 유의하십시오. 즉, 단락 회로 모나드 트랜스포머 (예 : ErrorT)를 사용하는 경우 예상대로 동작하지 않을 수 있습니다. 이것이 설계상의 결함인지 오용인지는 해석이 가능하지만이를 알고 있어야합니다. 자세한 내용은 http://andersk.mit.edu/haskell/monad-peel/를 참조하십시오. –

답변

4

monad-control 패키지가이 작업을 수행합니다. 나는 당신이 liftIOOp_에서 Control.Monad.IO.Control으로 기능을 원한다고 생각합니다.

특히

,

liftIOOp_ (withArgs newArgs) f 

당신이 원하는 일을해야한다. liftIOOp 기능을 사용하여 bracket과 같은 항목을 들어 올릴 수도 있습니다.

+0

에이스! 필요한 것. 감사! –

4

나는 interleavableIO package이이 문제를 해결한다고 믿습니다. 그것은 this cafe thread에서 논의됩니다.

+0

불행히도 매우 혼란 스러울 수 있습니다. 어떻게 사용됩니까? –

+0

@strake, 불행히도, 나는 그것을 사용한 적이 없다, 나는 단지 그것이 논의되고 기억하고있다. 어쩌면 또 다른 의문점이있다. – luqui

8

당신이 찾고있는 부분은 모나드 변이를 모나드 변압기로 끌어 올리는 것입니다. n-m에서 모나드 이체 동형 f 주어진 말을하는 것입니다

class MonadHoist t where 
    hoist :: (Monad m, Monad n) => (forall a. m a -> n a) -> t m a -> t n a 

    t :: Monad m => t Identity a -> t m a 
    t = hoist (return . runIdentity) 

, 당신은 호이스트를 사용하여 t nt m에서 모나드 이체 동형를 얻을 수 있습니다.

모나드 유사 동형 성은 위의 유형보다 약간 강하다. 즉 모나드 법칙을 유지하는 책임이있다.

f . return = return 
f . fmap g = fmap g . f 
f . join = join . f . fmap f 
     = join . fmap f . f -- by the second law 
     = (>>= f) . f  -- >>= in terms of join 

공지 사항 나는 hoist의 종류에 몰래 정량, MonadHoist 거의 모든 인스턴스에 대한 그 유연성을 필요로 판명! (Reader은 그렇지 않은 경우 일 수 있습니다.) MaybeT를 쓰지 않고 시도하십시오.

모나드 변환기는 일반적으로이 클래스를 인스턴스화 할 수 있습니다. 예를 들어 :

instance MonadHoist (StateT s) where 
    hoist f (StateT m) = StateT (f . m) 

instance MonadHoist (ReaderT e) where 
    hoist f (ReaderT m) = ReaderT (f . m) 

instance MonadHoist MaybeT where 
    hoist f (MaybeT m) = MaybeT (f m) 

그것은 Rank2Type을 필요로하기 때문에 우리는 현재 transformers 또는 mtl 패키지를 제공하지 않습니다,하지만 구현하기가 매우 간단합니다.

충분한 수요가있는 경우 monad-extras 패키지로 기꺼이 포장 해 드리겠습니다.

게시자의 게시물 주제에 입력 된 질문에 대한 답변이 있지만 질문과 관련된 텍스트의 대부분이 반영하지 않았기 때문에 부분을 말했습니다.

이를 위해 luqui의 조언을 따르기를 원할 것입니다. =)

+0

좋은 아이디어. 사실 (방금 알아 챘 듯이) 누군가 다른 사람이 그것을 깨달았습니다 : http://hackage.haskell.org/package/mmtl –

0

뿐만 아니라, 원하는 효과를 얻기 위해 runReaderT을 사용할 수있는 것 같다

*> :t withArgs [] (runReaderT f FooBar) 
withArgs [] (runReaderT f FooBar) :: IO Answer 
FooBar 일부 데이터 생성자이다

f는 위와 같이 정의된다.