Interruptable state
Feb. 19th, 2009 08:35 pmПонадобилось мне состояние, вычисление которого можно прервать в любой момент. Комбинатор
Вот как тут описать комбинатор
По моему, как не опишешь, зацикливания не избежать.
Пока обхожусь самодельной монадой
Значение монады заворачивается каждый раз в
Использование функции
Реализуем функции работы с состоянием a la
Всяческие
Прерывание, как уже говорилось, возвращает
Пример сверху теперь сработает корректно
Обидно, что трансформер, схожий с
guard не подходит тем, что его просто нет для State. if/then/else не устравает, потому что иногда прерывать необходимо в любом месте, например, в одной из вызываемой функции рекурсивной функции:
loop = do
modify (+1)
check
loop
check = do
s <- get
if s == 42
then interrupt
else return ()
Вот как тут описать комбинатор
interrupt?По моему, как не опишешь, зацикливания не избежать.
State на этом коде зацикливается. Можно попробовать StateT s Maybe, он оно не вернёт мне состояния на момент прерывания, когда я позову execStateT. Может быть можно всё таки воспользоваться готовым кодом, и просто параметризовать его нужным образом?Пока обхожусь самодельной монадой
newtype InterruptState s a = IntState { runIntState :: s -> (Maybe a, s) }
Значение монады заворачивается каждый раз в
Maybe, Nothing сигнализирует нам о том, что состояние было прервано. Для пользователя работа со значением должна быть прозрачна. Он ничего не обязан знать о том, что наше состояние реализовано через Maybe.
instance Functor (InterruptState s) where
fmap f (IntState fs) = IntState $ \s -> let (a, s') = fs s
in maybe (Nothing, s') (\v -> (Just (f v), s')) a
instance Monad (InterruptState s) where
return x = IntState $ \s -> (Just x, s)
m >>= f = IntState $ \s -> let (a, s') = runIntState m s
-- Вот здесь прерываем обработку, если состояние прервано.
in maybe (Nothing, s') (\v -> runIntState (f v) s') a
Использование функции
maybe позволяет разделить дальнейшее вычисление состояния и прерывание вычисления с возвратом текущего состояния. В качестве бонуса - мы используем чистое значение из под Just-конструктора в дальнейшей обработке.Реализуем функции работы с состоянием a la
State:instance MonadState s (InterruptState s) where get = IntState $ \s -> (Just s,s) put s = IntState $ \_ -> (Just (),s) execIntState m s = snd (runIntState m s) evalIntState m s = fst (runIntState m s)
Всяческие
withState, mapState реализуются схожим образом. Не забываем только учитывать Nothing.Прерывание, как уже говорилось, возвращает
Nothing в значении.interrupt = IntState $ \s -> (Nothing, s)
Пример сверху теперь сработает корректно
*Main> execIntState loop 0 42
Обидно, что трансформер, схожий с
StateT из этого не сделаешь, чтобы Maybe заменить на Monad m => m. Или сделаешь?
no subject
Date: 2009-02-19 06:36 pm (UTC)import Control.Monad.Cont import Control.Monad.State abort :: Monad m => r -> ContT r m a abort x = ContT $ \_ -> return x interrupt :: Monad m => ContT () m () interrupt = abort () runInterruptible m = runState (runContT m return) ------------------------------ loop = do modify (+1) check loop check :: ContT () (State Int) () check = do s <- get if s == 42 then interrupt else return ()no subject
Date: 2009-02-19 06:44 pm (UTC)no subject
Date: 2009-02-19 06:46 pm (UTC)no subject
Date: 2009-02-19 07:06 pm (UTC)no subject
Date: 2009-02-19 06:44 pm (UTC)Спасибо!
no subject
Date: 2009-02-19 06:47 pm (UTC)no subject
Date: 2009-02-19 06:51 pm (UTC)no subject
Date: 2009-02-19 07:26 pm (UTC)no subject
Date: 2009-02-19 07:27 pm (UTC)no subject
Date: 2009-02-19 07:28 pm (UTC)no subject
Date: 2009-02-19 07:29 pm (UTC)Спасибо! Пойду учиться :-)
no subject
Date: 2009-02-19 08:14 pm (UTC)no subject
Date: 2009-02-19 08:16 pm (UTC)И ещё - в scheme есть state monad? ;)
no subject
Date: 2009-02-19 08:26 pm (UTC)Другое дело, что с подобными вещами (монады, pattern matching) все-таки не так приятно работать в языке c динамической типизацией.
no subject
Date: 2009-02-19 08:28 pm (UTC)no subject
Date: 2009-02-19 09:18 pm (UTC)Но как две задействовать с удобством я пока не придумал.
no subject
Date: 2009-02-19 08:36 pm (UTC)Просто наиболее широко используемый оператор call/cc не слишком удобен. Тебе бы пришлось на верхнем уровне захватить продолжение и везде передавать как аргумент.
>И ещё - в scheme есть state monad? ;)
Банальность, но все равно скажу - монады есть везде, где есть first-class функции. А вот do-нотация делается или макросами, или через shift/reset.
no subject
Date: 2009-02-19 08:39 pm (UTC)Я же имел в виду - на фига козе баян? Что с этой монадой делать в схеме, где и так есть состояние?
no subject
Date: 2009-02-19 08:43 pm (UTC)>Что с этой монадой делать в схеме, где и так есть состояние?
Чисто теоретически, можно сделать какое-то особенное состояние. Например, мне недавно подумалось, что на shift/reset элементарно реализуется STM, но идею не проверял.
no subject
Date: 2009-02-19 08:45 pm (UTC)no subject
Date: 2009-02-23 02:51 pm (UTC)В моем понимании STM = StateT TransactionLog IO. Поэтому при переводе на Схему продолжения (или монада State) нам могут понадобиться только в том случае, если мы захотим протаскивать лог транзакции неявно, в противном случае достаточно мутабельных переменных и first-class функций.
Если бы моя гипотеза подтвердилась, я бы написал развернутый пост, а так, если интересно
вот наивная реализация (http://sites.google.com/site/iampalmmute/Home/myintstm.hs?attredirects=0). Не исключено, что с ошибками.
no subject
Date: 2009-02-23 07:12 pm (UTC)Я бегло посмотрел - там дедлок не будет в случае
Во время validateAndCommit в первом треде залочится var1 мьютекс, а во-втором var2 и будут ждать друг друга? Возможно, лучше tryTakeMVar и если Nothing, то освобождаем все и на петлю. А так - прикольно, интересное решение. Я, когда разбирался, делал похожее, но с одним глобальным мьютексом.
no subject
Date: 2009-02-23 07:13 pm (UTC)no subject
Date: 2009-02-24 01:54 pm (UTC)Продолжений там нет. План проверки идеи был такой - нарисовать на Хаскеле монаду, реализующую абстракцию STM штатными средствами. В зависимости от заковыристости монады решаем, нужны ли нам продолжения при переводе на Схему. Для StateT s IO можно обойтись без продолжений.
>Во время validateAndCommit в первом треде залочится var1 мьютекс, а во-втором var2 и будут ждать друг друга?
Дедлоков быть не должно - все переменные пронумерованы, Map.keys возвращает ключи отсортированными - мюьтексы захыватываются в одном порядке.
>Я, когда разбирался, делал похожее, но с одним глобальным мьютексом.
Ну, с глобальным мьютексом можно вообще сердито поступить - STM = IO, atomically m = do { lock gmtx; a <- m; release gmtx; return a } :) (я так понимаю, мьютекс захватывался только во время коммита?)
no subject
Date: 2009-02-24 01:55 pm (UTC)Да, во время коммита.
no subject
Date: 2009-02-19 09:23 pm (UTC)Правда у меня там уже есть монадический парсер, а пореюзать синтаксис для второй монады у меня не получилось.
no subject
Date: 2009-02-19 09:25 pm (UTC)no subject
Date: 2009-02-19 09:32 pm (UTC)На работе это основной язык, решил не разрываться.
no subject
Date: 2009-02-19 09:35 pm (UTC)А почему монада показалась лучше, чем переменные? Вроде записи то у клиента схожие.
no subject
Date: 2009-02-19 09:43 pm (UTC)no subject
Date: 2009-02-19 09:45 pm (UTC)no subject
Date: 2009-02-19 08:38 pm (UTC)no subject
Date: 2009-02-19 06:43 pm (UTC)no subject
Date: 2009-02-19 07:20 pm (UTC)no subject
Date: 2009-02-19 07:34 pm (UTC)loop -- он таки хвостато-рекурсивен или нет?
no subject
Date: 2009-02-19 07:36 pm (UTC)no subject
Date: 2009-02-19 07:39 pm (UTC)no subject
Date: 2009-02-19 07:48 pm (UTC)no subject
Date: 2009-02-20 09:51 am (UTC)no subject
Date: 2009-02-20 09:52 am (UTC)no subject
Date: 2009-02-20 10:04 am (UTC)no subject
Date: 2009-02-20 11:34 am (UTC)no subject
Date: 2009-02-20 12:50 pm (UTC)> Теоретически Cont должен быть дешевле, чем Maybe или Error, - меньше сравнений с образцом.
М-м-м... не факт. Зато дофига cons-ов, от которых производительность может просесть очень прилично.
no subject
Date: 2009-02-20 02:49 pm (UTC)Слишком категорично. Выпрыгивать из вложенных циклов - вполне естественное применение продолжений.
>М-м-м... не факт. Зато дофига cons-ов, от которых производительность может просесть очень прилично.
Вот тут ты, похоже, прав.