Я пытаюсь определить семейство государственных машин с несколько различными типами состояний. В частности, более «сложные» конечные автоматы имеют состояния, которые формируются путем объединения состояний более простых конечных автоматов.
(Это похоже на объектно-ориентированную настройку, где объект имеет несколько атрибутов, которые также являются объектами.)
Вот упрощенный пример того, чего я хочу достичь.
data InnerState = MkInnerState { _innerVal :: Int }
data OuterState = MkOuterState { _outerTrigger :: Bool, _inner :: InnerState }
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
i <- _innerVal <$> get
put $ MkInnerState (i + 1)
return i
outerStateFoo :: Monad m => StateT OuterState m Int
outerStateFoo = do
b <- _outerTrigger <$> get
if b
then
undefined
-- Here I want to "invoke" innerStateFoo
-- which should work/mutate things
-- "as expected" without
-- having to know about the outerState it
-- is wrapped in
else
return 666
В более общем смысле я хочу обобщенную структуру, в которой эти вложения являются более сложными. Вот то, что я хочу знать, как это сделать.
class LegalState s
data StateLess
data StateWithTrigger where
StateWithTrigger :: LegalState s => Bool -- if this trigger is `True`, I want to use
-> s -- this state machine
-> StateWithTrigger
data CombinedState where
CombinedState :: LegalState s => [s] -- Here is a list of state machines.
-> CombinedState -- The combinedstate state machine runs each of them
instance LegalState StateLess
instance LegalState StateWithTrigger
instance LegalState CombinedState
liftToTrigger :: Monad m, LegalState s => StateT s m o -> StateT StateWithTrigger m o
liftToCombine :: Monad m, LegalState s => [StateT s m o] -> StateT CombinedState m o
Для контекста это то, чего я хочу достичь с помощью этого механизма:
Я хочу создать такие вещи, как «потоковые трансформеры», которые в основном являются функциями с состоянием: они потребляют токен, изменяют свое внутреннее состояние и что-то выводят. В частности, меня интересует класс Stream Transformers, где выводом является логическое значение; мы будем называть эти «мониторы».
Сейчас я пытаюсь разработать комбинаторы для этих объектов. Некоторые из них:
pre
Комбинатор. Предположим, чтоmon
это монитор. Затемpre mon
это монитор, который всегда выдаетFalse
после использования первого токена, а затем имитирует поведение,mon
как будто предыдущий токен вставляется сейчас. Я хотел бы смоделировать состояниеpre mon
сStateWithTrigger
в приведенном выше примере, так как новое состояние является логическим, наряду с исходным состоянием.and
Комбинатор. Предположим, чтоm1
иm2
есть мониторы. Затемm1 `and` m2
монитор, который подает токен в m1, а затем в m2, а затем производитTrue
если оба ответа верны. Я хотел бы смоделировать состояниеm1 `and` m2
сCombinedState
в приведенном выше примере, так как необходимо поддерживать состояние обоих мониторов.
источник
_innerVal <$> get
это простоgets _innerVal
(какgets f == liftM f get
иliftM
толькоfmap
специализируется на монады).StateT InnerState m Int
значение в первую очередьouterStateFoo
?zoom
для этого.Ответы:
На ваш первый вопрос, как упомянул Карл,
zoom
отlens
делает именно то, что вы хотите. Ваш код с линзами можно написать так:Изменить: Пока мы на это, если вы уже вносите,
lens
тоinnerStateFoo
можно написать так:источник
Я думаю, что то, чего ты хочешь достичь, не требует много машин.
Это
StreamTransformer
не обязательно с состоянием, но допускает состояние с состоянием. Вам не нужно (и IMO не должно! В большинстве случаев !!) обращаться к классам типов, чтобы определить их (или даже когда-либо! :), но это уже другая тема).источник
pre st = stateful (Nothing, st) k where k i (s,st) = let (o, st') = runStreamTransformer st i in ( maybe False id s , (Just o, st'))
.