2011-07-06 32 views
9

Tiêu đề gây nhầm lẫn cho một câu hỏi khó hiểu! Tôi hiểu a) monads, b) đơn nguyên IO, c) đơn vị Cont (Control.Monad.Cont) và d) đơn biến áp tiếp tục ContT. (Và tôi mơ hồ hiểu các biến thế đơn nguyên nói chung - mặc dù không đủ để trả lời câu hỏi này.) Tôi hiểu cách viết một chương trình ở đó tất cả các chức năng đang ở chế độ Đơn nguyên (Cont r a) và tôi hiểu cách viết chương trình trong đó tất cả các chức năng nằm trong đơn vị Cont/IO kết hợp (ContT r IO a).Thoát khỏi đơn nguyên IO bên trong đơn vị Continuation

Nhưng tôi đang tự hỏi làm thế nào tôi có thể viết một chương trình mà một số chức năng đang ở trong một Tiếp/IO đơn nguyên kết hợp (ContT r IO a) và chức năng khác chỉ trong đơn nguyên Tiếp (Cont r a). Về cơ bản, tôi muốn viết toàn bộ chương trình theo kiểu tiếp tục, nhưng chỉ sử dụng đơn nguyên IO khi cần thiết (giống như trong mã Haskell "thông thường", tôi chỉ sử dụng đơn nguyên IO khi cần thiết).

Ví dụ xem xét hai chức năng này, trong phong cách không tiếp tục:

foo :: Int -> IO Int 
foo n = do 
    let x = n + 1 
    print x 
    return $ bar x 

bar :: Int -> Int 
bar m = m * 2 

Lưu ý rằng foo đòi hỏi IO nhưng bar là tinh khiết. Bây giờ tôi đã tìm ra cách viết mã này sử dụng đầy đủ các đơn nguyên tiếp tục, nhưng tôi cần phải sợi IO qua bar cũng như:

foo :: Int -> ContT r IO Int 
foo n = do 
    let x = n + 1 
    liftIO $ print x 
    bar x 

bar :: Int -> ContT r IO Int 
bar m = return $ m * 2 

tôi làm muốn tất cả các mã của tôi trong phong cách tiếp tục, nhưng tôi don' t muốn sử dụng trình đơn IO trên các chức năng không yêu cầu. Về cơ bản, tôi muốn để xác định bar như thế này:

bar :: Int -> Cont r Int 
bar m = return $ m * 2 

Thật không may, tôi không thể tìm thấy một cách để gọi một hàm Cont r a đơn nguyên (bar) từ bên trong một hàm ContT r IO a đơn nguyên (foo). Có cách nào để "nâng" một đơn nguyên không biến đổi thành một biến đổi không? tức là, làm cách nào tôi có thể thay đổi dòng "bar x" trong foo để nó có thể gọi chính xác bar :: Int -> Cont r Int?

Trả lời

17

Đây là nơi Control.Monad.Class do thỏa thuận hợp Hãy bar đa hình trong những gì đơn nguyên nó có thể làm việc tại:.

bar :: MonadCont m => Int -> m Int 
bar m = return $ m * 2 

Lưu ý rằng danh sách các trường hợp ở dưới cùng của trang hiển thị rằng trường hợp của MonadCont nổi tiếng vào thời điểm đó tài liệu được tạo bao gồm cả Cont rMonad m => ContT r m. Ngoài ra, lớp MonadCont là những gì xác định hàm callCC, đó là những gì cần thiết để sử dụng các tính năng tiếp tục. Điều này có nghĩa là bạn có thể sử dụng tính biểu cảm đầy đủ của sự tiếp tục trong phạm vi bar, mặc dù ví dụ này không.

Bằng cách này, bạn viết các hàm không thể sử dụng IO, vì chúng không có ràng buộc MonadIO, cũng như kiểu của chúng không đề cập rõ ràng IO. Nhưng chúng có tính đa hình trong đó monad chúng làm việc bên trong, như vậy chúng có thể được gọi là trivially từ các bối cảnh bao gồm IO.

+1

Cảm ơn. Điều đó hoạt động. Tôi cũng tìm thấy giải pháp của riêng mình, mà đã cho tôi chính xác những gì tôi muốn (tôi không phải thay đổi 'Bar'):' liftCont :: Cont (m r) a -> ContT r m a'; 'liftCont c = ContT $ runCont c'. Giải pháp của tôi giải nén 'Cont' và xây dựng một' ContT'. Tôi nghĩ rằng giải pháp của bạn đẹp hơn vì nó đa hình và không yêu cầu thao tác thực sự của cấu trúc dữ liệu, vì vậy hãy đánh dấu vào bạn. Nhưng tôi sẽ đăng bài của tôi như một câu trả lời khác, vì nó hữu ích trong trường hợp bạn không thể sửa đổi 'bar'. Ngoài ra 1 để giải thích lý do tại sao nó sẽ không thể sử dụng IO trong 'bar'. – mgiuca

5

tôi thấy rằng đây thực hiện chính xác những gì tôi muốn (mà không cần phải thay đổi Bar):

liftCont :: Cont (m r) a -> ContT r m a 
liftCont = ContT . runCont 

này giải nén Cont và xây dựng một ContT.

tôi sau đó có thể sử dụng liftCont gọi Bar từ Foo:

foo n = do 
    let x = n + 1 
    liftIO $ print x 
    liftCont $ bar x 

Tôi không nghĩ rằng đây là "đẹp hơn" so với giải pháp của Carl (tôi đã đưa ông đánh dấu), nhưng tôi đăng nó ở đây vì nó cho phép bạn sử dụng Bar mà không sửa đổi loại của nó, vì vậy hữu ích nếu bạn không thể sửa đổi Bar. (Nó có thể có hiệu suất tồi tệ hơn mặc dù.)

Các vấn đề liên quan