Как можно реализовать пробный улов в Haskell? - PullRequest
3 голосов
/ 29 февраля 2012

Я хочу написать функцию

forkos_try :: IO (Maybe α) -> IO (Maybe α)

, которая принимает команду x.x является обязательной операцией, которая сначала изменяет состояние, а затем проверяет, не испорчено ли это состояние. (Он не делает ничего внешнего, что потребует какой-либо изолированной среды на уровне ОС для восстановления состояния.)

  • , если x оценивается как Just y, forkos_try возвращает Just y.
  • в противном случае forkos_try откатывает состояние и возвращает Nothing.

Внутренне он должен fork()в потоки parent и child, с x, работающим на child.

  • , если x успешно, child должен продолжать работать (возвращая результат xparent должен умереть
  • в противном случае parent должен продолжать работать (возвращая Nothing), а child должен умереть

Вопрос : Что такоеспособ написать что-то с эквивалентной или более мощной семантикой, чем forkos_try? NB - мутировавшее состояние (x) находится во внешней библиотеке и не может передаваться между потоками .Следовательно, важна семантика того, какой поток следует поддерживать живым.

Формально «продолжать работать» означает «выполнить некоторое продолжение rest :: Maybe α -> IO ()».Но это продолжение нигде явно не указано в коде.

В моем случае, я думаю, что (пока) будет работать написать его в другом стиле, используя forkOS (который занимает все вычисленияchild будет выполняться), так как я могу написать явное выражение для rest.Но меня беспокоит то, что я не могу понять, как это сделать с примитивной функцией forkOS - можно подумать, что она будет достаточно общей для поддержки любого конкретного случая (который может выглядеть как высокоуровневый API, например forkos_try).

РЕДАКТИРОВАТЬ - см. Пример кода с явным rest, если проблема все еще не ясна [http://pastebin.com/nJ1NNdda].

PS Я давно не писал код параллелизма;надеюсь, мои знания о POSIX fork() верны!Заранее спасибо.

1 Ответ

2 голосов
/ 29 февраля 2012

Ситуация гораздо проще рассуждать, если вы моделируете состояние явно.

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 и т. д.).Невозможно повернуть вспять некоторые вещи (запустить ракеты, записать на стандартный вывод и т. Д.), Поэтому я вижу здесь один из двух вариантов:

  1. клонировать мир и запустить действие впесочница.Если это удастся, тогда иди вперед и запусти действие в реальном мире.
  2. клонируй мир и запусти действие в реальном мире.Если это не удастся, замените Реальный мир снимком, который вы сделали ранее.

Конечно, оба эти подхода на самом деле одинаковы: раскошелиться на мир.Один мир управляет действием, другой - нет.Если действие успешно, тогда этот мир продолжается;в противном случае другой мир продолжается.Вы предлагаете сделать это, опираясь на 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 может поместитьвернитесь назад.) Этот подход может быть не самым эффективным, но он очень прост.

...