Ситуация гораздо проще рассуждать, если вы моделируете состояние явно.
someStateFunc :: (s -> Maybe (a, s))
-- inside some other function
case someStateFunc initialState of
Nothing -> ... -- it failed. stick with initial state
Just (a, newState) -> ... -- it suceeded. do something with
-- the result and new state
В неизменном состоянии «откат» прост: просто продолжайте использовать initialState
.И «не откат» также прост: просто используйте newState
.
Итак ... Я предполагаю из вашего объяснения, что эта «внешняя библиотека» выполняет некоторые нетривиальные эффекты ввода-вывода, которые, тем не менее, ограниченымалоизвестных и обратимых операций (изменение файла, IORef и т. д.).Невозможно повернуть вспять некоторые вещи (запустить ракеты, записать на стандартный вывод и т. Д.), Поэтому я вижу здесь один из двух вариантов:
- клонировать мир и запустить действие впесочница.Если это удастся, тогда иди вперед и запусти действие в реальном мире.
- клонируй мир и запусти действие в реальном мире.Если это не удастся, замените Реальный мир снимком, который вы сделали ранее.
Конечно, оба эти подхода на самом деле одинаковы: раскошелиться на мир.Один мир управляет действием, другой - нет.Если действие успешно, тогда этот мир продолжается;в противном случае другой мир продолжается.Вы предлагаете сделать это, опираясь на forkOS
, который клонировал бы все состояние программы, но этого было бы недостаточно, чтобы иметь дело, например, с изменениями файлов.Позвольте мне предложить вместо этого подход, более близкий к простоте неизменяемого состояния:
tryIO :: IO s -> (s -> IO ()) -> IO (Maybe a) -> IO (Maybe a)
tryIO save restore action = do
initialState <- save
result <- action
case result of
Nothing -> restore initialState >> return Nothing
Just x -> return (Just x)
Здесь вы должны предоставить некоторую структуру данных s
и путь к save
и restore
изуказанная структура данных.Это позволяет вам гибко выполнять любое клонирование, которое вам необходимо.(Например, save
может скопировать определенный файл во временное местоположение, а затем restore
может скопировать его обратно и удалить временный файл. Или save
может скопировать значение определенных IORefs, а затем restore
может поместитьвернитесь назад.) Этот подход может быть не самым эффективным, но он очень прост.