EDITED 2015/11/29: xem dướiMemoizing và lặp lại IO monads
Tôi đang cố gắng để viết một ứng dụng mà có một nút do-cuối-action-một lần nữa. Lệnh được đề cập có thể yêu cầu đầu vào, và suy nghĩ của tôi về cách thực hiện điều này là chỉ chạy lại đơn vị kết quả với IO ghi nhớ.
Có rất nhiều bài đăng trên SO với các câu hỏi tương tự, nhưng không có giải pháp nào có vẻ hoạt động ở đây.
Tôi đã nâng mã memoIO
từ this SO answer và thay đổi triển khai để chạy trên MonadIO
.
-- Memoize an IO function
memoIO :: MonadIO m => m a -> m (m a)
memoIO action = do
ref <- liftIO $ newMVar Nothing
return $ do
x <- maybe action return =<< liftIO (takeMVar ref)
liftIO . putMVar ref $ Just x
return x
Tôi đã có một repro nhỏ của phương pháp ứng dụng của tôi, sự khác biệt duy nhất là ứng dụng của tôi có một lớn ngăn xếp biến thay vì chỉ chạy trong IO
:
-- Global variable to contain the action we want to repeat
actionToRepeat :: IORef (IO String)
actionToRepeat = unsafePerformIO . newIORef $ return ""
-- Run an action and store it as the action to repeat
repeatable :: IO String -> IO String
repeatable action = do
writeIORef actionToRepeat action
action
-- Run the last action stored by repeatable
doRepeat :: IO String
doRepeat = do
x <- readIORef actionToRepeat
x
Ý tưởng hạnh phúc Tôi có thể lưu trữ một hành động với ghi nhớ IO
trong một IORef
(qua repeatable
) khi tôi ghi lại những gì đã được thực hiện lần cuối và sau đó thực hiện lại nó với doRepeat
.
tôi thử nghiệm này qua:
-- IO function to memoize
getName :: IO String
getName = do
putStr "name> "
getLine
main :: IO()
main = do
repeatable $ do
memoized <- memoIO getName
name <- memoized
putStr "hello "
putStrLn name
return name
doRepeat
return()
với sản lượng dự kiến:
name> isovector
hello isovector
hello isovector
nhưng đầu ra thực tế:
name> isovector
hello isovector
name> wasnt memoized
hello wasnt memoized
Tôi không hoàn toàn chắc chắn những gì vấn đề là, hoặc thậm chí làm thế nào để đi về gỡ lỗi này. Gun vào đầu tôi, tôi cho rằng việc đánh giá lười biếng đang cắn tôi ở đâu đó, nhưng tôi không thể tìm ra được đâu.
Cảm ơn trước!
EDIT 2015/11/29: trường hợp mục đích sử dụng của tôi cho điều này là để thực hiện các repeat last change nhà điều hành trong một vim-clone. Mỗi hành động có thể thực hiện một số tùy ý các cuộc gọi IO tùy ý, và tôi muốn nó có thể xác định những cái nào cần được ghi nhớ (đọc một tập tin, có lẽ không. Yêu cầu người dùng nhập liệu, có).
Bạn có muốn chạy lại hành động hoặc bạn muốn trả về kết quả của hành động cuối cùng không? Đây là một sự khác biệt quan trọng. Nếu sau này, bạn muốn ghi nhớ giá trị trả về của hành động cuối cùng, trong khi nếu trước đó, bạn muốn ghi nhớ toàn bộ hành động. Ví dụ: nếu bạn muốn đọc tệp và trả lại nội dung của tệp, bạn có muốn đọc lại tệp (có thể nhận dữ liệu được cập nhật) hay chỉ trả về nội dung được lưu trong bộ nhớ cache không? –
Tôi muốn phát lại hành động (trong BigMonadStackT IO tùy ý), với bộ nhớ đệm (chỉ trả về kết quả của) các cuộc gọi IO bên trong nó. Trường hợp sử dụng dự định là để thực hiện toán tử [lặp lại thay đổi cuối cùng] (vd: http://vim.wikia.com/wiki/Repeat_last_change) trong vim, yêu cầu đầu vào chỉ khi bạn chạy nó lần đầu tiên. –
Tôi tin rằng điều này có một chút vấn đề. Nếu bạn quyết định chỉ ghi nhớ một số cuộc gọi, điều gì sẽ xảy ra nếu cuộc gọi không ghi nhớ khác, thay đổi luồng điều khiển? Ví dụ: nếu đầu vào của người dùng là tệp nào nên được đọc? –