CURA 2015/11/29: vedi fondoMemoizing e ripetendo IO monadi
Sto cercando di scrivere un'applicazione che ha un pulsante do-ultima-azione-di nuovo. Il comando in questione può chiedere input, e il mio pensiero su come ottenere questo risultato è stato solo rieseguire la monade risultante con IO memorizzato.
Ci sono molti post su SO con domande simili, ma nessuna delle soluzioni sembra funzionare qui.
Ho sollevato il codice memoIO
da this SO answer e modificato l'implementazione per eseguire 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
Ho un piccolo Repro dell'approccio di mia app, l'unica vera differenza è la mia applicazione ha un grosso stack trasformatore invece di correre in 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
L'idea dell'essere Posso memorizzare un'azione con memoized IO
in un IORef
(tramite repeatable
) quando registro ciò che è stato fatto l'ultima volta, quindi lo eseguo nuovamente con doRepeat
.
I test di questa via:
-- 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()
con uscita prevista:
name> isovector
hello isovector
hello isovector
ma effettiva di uscita:
name> isovector
hello isovector
name> wasnt memoized
hello wasnt memoized
io non sono del tutto sicuro qual è il problema, o anche come fare per eseguire il debug di questo. Spara alla mia testa, suppongo che la valutazione pigra mi morde da qualche parte, ma non riesco a capire dove.
Grazie in anticipo!
EDIT 2015/11/29: Il mio caso destinazione d'uso per questo è di implementare l'operatore repeat last change in vim-clone. Ogni azione può eseguire un numero arbitrario di chiamate IO arbitrarie, e vorrei che fosse in grado di specificare quali dovrebbero essere memoizzate (leggendo un file, probabilmente no, chiedendo all'utente di inserire input, sì).
Si desidera rieseguire l'azione o si desidera restituire il risultato dell'ultima azione? Questa è una differenza importante. Se quest'ultimo, si desidera memoize il valore di ritorno dell'ultima azione, mentre se il primo, si desidera ricordare l'intera azione. Ad esempio, se si desidera leggere un file e restituirne il contenuto, si desidera leggere nuovamente il file (possibilmente ottenere dati aggiornati) o semplicemente restituire il contenuto memorizzato nella cache? –
Voglio ripetere l'azione (in I/O BigMonadStackT arbitrario), con cache (restituendo solo il risultato di) chiamate IO al suo interno. Il caso d'uso previsto è implementare l'operatore [ripeti l'ultimo cambiamento] (http://vim.wikia.com/wiki/Repeat_last_change) in vim, che richiede l'input solo la prima volta che lo esegui. –
Credo che questo sia un po 'problematico. Se si decide di memoizzare solo alcune chiamate, cosa succede se le altre chiamate non memorizzate modificano il flusso di controllo? Ad esempio, cosa succede se l'input dell'utente è quale file deve essere letto? –