2011-07-06 12 views
5

Vấn đề là vấn đề này. Tôi có:mapMonadTrans :: MonadTrans xT => (m a -> n b) -> xT m a -> xT n b

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

Tôi cần chạy điều này với các đối số đã sửa đổi. Tuy nhiên, vì m là không rõ, tôi có thể không chỉ đơn giản là sử dụng

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

kể từ khi tôi cần bằng cách nào đó để transform (withArgs args) vào m cho tất cả m.

Một khả năng tôi thấy là xác định withArgs của riêng tôi, như sau:

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; 
}; 

Tuy nhiên, đây là một kludge, và cụ thể cho một chức năng - Tôi sẽ cần phải viết lại mỗi withX :: X -> IO a -> IO a, ví dụ Control.Exception.handle

Điều gì, nếu có, là cách tốt hơn để làm điều này?

Chỉnh sửa: Trong trường hợp xử lý, tôi tìm thấy Control.Monad.CatchIO. Trong trường hợp khác, tôi đã sử dụng một cái khác, briefer kludge (không đáng để đăng) để tránh kludge ở trên. Vẫn đang tìm kiếm một giải pháp tốt hơn!

+0

Điều gì xảy ra nếu bạn xóa chữ ký loại trên 'f'? Tôi tự hỏi nếu ràng buộc với 'MonadIO' là quá hạn chế. –

+0

Tôi cần làm I/O trong 'f'. Nếu không, đó sẽ là lớn. (Trong thực tế, tôi có một kiểu dữ liệu a với một hàm trong nó để mang lại một số giá trị của loại b, và hàm phải đủ chung để một số giá trị của kiểu a có thể làm I/O để sinh lợi b.) –

+0

@strake : lưu ý rằng có sự cố với Control.Monad.CatchIO. Cụ thể là, nếu bạn đang sử dụng một máy biến áp ngắn mạch (ví dụ: ErrorT), nó có thể cho nó không hoạt động như bạn mong đợi. Cho dù đây là một lỗ hổng thiết kế hoặc lạm dụng được mở để giải thích, nhưng bạn nên nhận thức được nó. Xem http://andersk.mit.edu/haskell/monad-peel/ để biết chi tiết. –

Trả lời

4

Gói monad-control sẽ thực hiện việc này. Tôi nghĩ bạn muốn có chức năng liftIOOp_ từ Control.Monad.IO.Control.

Cụ thể,

liftIOOp_ (withArgs newArgs) f 

nên làm những gì bạn muốn. Bạn cũng có thể nâng những thứ như bracket, với chức năng liftIOOp.

+0

Ace! Chỉ cần những gì tôi cần. Cảm ơn! –

4

Tôi tin rằng interleavableIO package giải quyết vấn đề này. Nó được thảo luận trong this cafe thread.

+0

Nó có thể, nhưng, thật không may, nó rất phức tạp. Nó được sử dụng như thế nào? –

+0

@strake, Thật không may, tôi chưa bao giờ sử dụng nó, tôi chỉ nhớ nó đang được thảo luận. Có lẽ một câu hỏi SO là theo thứ tự? – luqui

8

Một phần của những gì bạn đang tìm kiếm là một cẩu của một đồng cấu đơn nguyên thành một biến áp đơn nguyên.

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) 

Đó là để nói, cho một đơn nguyên đồng cấu fm-n, bạn có thể có được một đồng cấu đơn nguyên từ t m để t n sử dụng tời.

Một đồng cấu đơn nguyên hơi mạnh hơn các loại trên thực thi, cụ thể là nó chịu trách nhiệm bảo toàn các luật đơn nguyên.

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 

Thông báo các lượng hóa mà tôi lẻn về loại hoist, MonadHoist hóa ra cần sự linh hoạt cho hầu hết các trường hợp! (Reader xảy ra là một trong những trường hợp nó không. Cố gắng viết MaybeT mà không có nó.)

Biến thế monad có thể, nói chung, nhanh chóng lớp này. Ví dụ:

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) 

Chúng tôi hiện không cung cấp nó trong transformers hoặc mtl gói vì nó sẽ đòi hỏi một Rank2Type, nhưng nó là khá đơn giản để thực hiện.

Nếu có đủ nhu cầu cho nó, tôi sẽ vui vẻ gói nó lên trong gói monad-extras.

Bây giờ, tôi đã nói một phần, bởi vì trong khi điều này trả lời câu hỏi được đưa ra trong chủ đề bài đăng của bạn, nó không giải quyết nhu cầu được phản ánh bởi phần lớn văn bản được liên kết với câu hỏi của bạn!

Vì lý do đó, bạn có thể muốn theo lời khuyên của luqui. =)

+0

Ý tưởng hay. Trên thực tế, (như tôi vừa để ý), ai đó đã nhận ra nó: http://hackage.haskell.org/package/mmtl –

0

Có vẻ như bạn có thể sử dụng runReaderT để có được hiệu quả mong muốn, cũng như:

*> :t withArgs [] (runReaderT f FooBar) 
withArgs [] (runReaderT f FooBar) :: IO Answer 

nơi FooBar là một số nhà xây dựng dữ liệu và f được định nghĩa như trên.