Saya mencoba untuk mendefinisikan keluarga mesin negara dengan jenis negara yang agak berbeda. Secara khusus, mesin negara yang lebih "kompleks" memiliki keadaan yang dibentuk dengan menggabungkan keadaan mesin negara yang lebih sederhana.
(Ini mirip dengan pengaturan berorientasi objek di mana objek memiliki beberapa atribut yang juga objek.)
Ini adalah contoh sederhana dari apa yang ingin saya capai.
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
Secara umum, saya ingin kerangka kerja umum di mana sarang ini lebih kompleks. Ini adalah sesuatu yang saya ingin tahu bagaimana melakukannya.
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
Untuk konteks, inilah yang ingin saya capai dengan mesin ini:
Saya ingin merancang hal-hal ini yang disebut "Stream Transformers", yang pada dasarnya adalah fungsi stateful: Mereka mengkonsumsi token, mengubah keadaan internal mereka dan mengeluarkan sesuatu. Khususnya, saya tertarik pada kelas Stream Transformers di mana outputnya adalah nilai Boolean; kami akan memanggil ini "monitor".
Sekarang, saya mencoba merancang kombinator untuk objek-objek ini. Beberapa dari mereka adalah:
- Sebuah
pre
combinator. Misalkan itumon
adalah monitor. Kemudian,pre mon
adalah monitor yang selalu menghasilkanFalse
setelah token pertama dikonsumsi dan kemudian meniru perilakumon
seolah-olah token sebelumnya sedang dimasukkan sekarang. Saya ingin memodelkan keadaanpre mon
denganStateWithTrigger
dalam contoh di atas karena negara baru adalah boolean bersama dengan keadaan aslinya. - Sebuah
and
combinator. Anggaplahm1
danm2
monitor. Kemudian,m1 `and` m2
adalah monitor yang mengumpan token ke m1, dan kemudian ke m2, dan kemudian menghasilkanTrue
jika kedua jawaban itu benar. Saya ingin memodelkan keadaanm1 `and` m2
denganCombinedState
dalam contoh di atas karena keadaan kedua monitor harus dipertahankan.
sumber
_innerVal <$> get
adilgets _innerVal
(sepertigets f == liftM f get
, danliftM
hanyafmap
khusus untuk monad).StateT InnerState m Int
nilai di tempat pertamaouterStateFoo
?zoom
.Jawaban:
Untuk pertanyaan pertama Anda, seperti yang disebutkan Carl,
zoom
darilens
lakukan persis apa yang Anda inginkan. Kode Anda dengan lensa dapat ditulis seperti ini:Sunting: Ketika kita sedang berada di sana, jika Anda sudah membawa
lens
makainnerStateFoo
dapat ditulis seperti:sumber
Saya pikir apa yang ingin Anda capai tidak membutuhkan banyak mesin.
Ini
StreamTransformer
tidak selalu stateful, tetapi mengakui yang stateful. Anda tidak perlu (dan IMO tidak boleh! Dalam kebanyakan kasus !!) meraih typeclasses untuk mendefinisikan ini (atau memang pernah! :) tapi itu topik lain).sumber
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'))
.