Entry tags:
Interruptable state
Понадобилось мне состояние, вычисление которого можно прервать в любой момент. Комбинатор
Вот как тут описать комбинатор
По моему, как не опишешь, зацикливания не избежать.
Пока обхожусь самодельной монадой
Значение монады заворачивается каждый раз в
Использование функции
Реализуем функции работы с состоянием 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
no subject
no subject
no subject
no subject
Спасибо!
no subject
no subject
no subject
no subject
no subject
no subject
Спасибо! Пойду учиться :-)
no subject
no subject
И ещё - в scheme есть state monad? ;)
no subject
Другое дело, что с подобными вещами (монады, pattern matching) все-таки не так приятно работать в языке c динамической типизацией.
no subject
no subject
Но как две задействовать с удобством я пока не придумал.
no subject
Просто наиболее широко используемый оператор call/cc не слишком удобен. Тебе бы пришлось на верхнем уровне захватить продолжение и везде передавать как аргумент.
>И ещё - в scheme есть state monad? ;)
Банальность, но все равно скажу - монады есть везде, где есть first-class функции. А вот do-нотация делается или макросами, или через shift/reset.
no subject
Я же имел в виду - на фига козе баян? Что с этой монадой делать в схеме, где и так есть состояние?
no subject
>Что с этой монадой делать в схеме, где и так есть состояние?
Чисто теоретически, можно сделать какое-то особенное состояние. Например, мне недавно подумалось, что на shift/reset элементарно реализуется STM, но идею не проверял.
no subject
no subject
В моем понимании STM = StateT TransactionLog IO. Поэтому при переводе на Схему продолжения (или монада State) нам могут понадобиться только в том случае, если мы захотим протаскивать лог транзакции неявно, в противном случае достаточно мутабельных переменных и first-class функций.
Если бы моя гипотеза подтвердилась, я бы написал развернутый пост, а так, если интересно
вот наивная реализация (http://sites.google.com/site/iampalmmute/Home/myintstm.hs?attredirects=0). Не исключено, что с ошибками.
no subject
Я бегло посмотрел - там дедлок не будет в случае
Во время validateAndCommit в первом треде залочится var1 мьютекс, а во-втором var2 и будут ждать друг друга? Возможно, лучше tryTakeMVar и если Nothing, то освобождаем все и на петлю. А так - прикольно, интересное решение. Я, когда разбирался, делал похожее, но с одним глобальным мьютексом.
no subject
no subject
Продолжений там нет. План проверки идеи был такой - нарисовать на Хаскеле монаду, реализующую абстракцию STM штатными средствами. В зависимости от заковыристости монады решаем, нужны ли нам продолжения при переводе на Схему. Для StateT s IO можно обойтись без продолжений.
>Во время validateAndCommit в первом треде залочится var1 мьютекс, а во-втором var2 и будут ждать друг друга?
Дедлоков быть не должно - все переменные пронумерованы, Map.keys возвращает ключи отсортированными - мюьтексы захыватываются в одном порядке.
>Я, когда разбирался, делал похожее, но с одним глобальным мьютексом.
Ну, с глобальным мьютексом можно вообще сердито поступить - STM = IO, atomically m = do { lock gmtx; a <- m; release gmtx; return a } :) (я так понимаю, мьютекс захватывался только во время коммита?)
no subject
Да, во время коммита.
no subject
Правда у меня там уже есть монадический парсер, а пореюзать синтаксис для второй монады у меня не получилось.
no subject
no subject
На работе это основной язык, решил не разрываться.
no subject
А почему монада показалась лучше, чем переменные? Вроде записи то у клиента схожие.
no subject
no subject
no subject
no subject
no subject
no subject
loop -- он таки хвостато-рекурсивен или нет?
no subject
no subject
no subject
no subject
no subject
no subject
no subject
no subject
> Теоретически Cont должен быть дешевле, чем Maybe или Error, - меньше сравнений с образцом.
М-м-м... не факт. Зато дофига cons-ов, от которых производительность может просесть очень прилично.
no subject
Слишком категорично. Выпрыгивать из вложенных циклов - вполне естественное применение продолжений.
>М-м-м... не факт. Зато дофига cons-ов, от которых производительность может просесть очень прилично.
Вот тут ты, похоже, прав.