Interruptable state
Feb. 19th, 2009 08:35 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Понадобилось мне состояние, вычисление которого можно прервать в любой момент. Комбинатор
Вот как тут описать комбинатор
По моему, как не опишешь, зацикливания не избежать.
Пока обхожусь самодельной монадой
Значение монады заворачивается каждый раз в
Использование функции
Реализуем функции работы с состоянием 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
. Или сделаешь?