Haskell'de İç içe Devletler


9

Bir çeşit farklı devlete sahip bir devlet makineleri ailesi tanımlamaya çalışıyorum. Özellikle, daha "karmaşık" durum makineleri, daha basit durum makinelerinin durumlarının birleştirilmesiyle oluşturulan durumlara sahiptir.

(Bu, bir nesnenin aynı zamanda nesne olan çeşitli özniteliklere sahip olduğu nesne yönelimli bir ayara benzer.)

İşte elde etmek istediğim şeyin basitleştirilmiş bir örneği.

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

Daha genel olarak, bu yuvalamaların daha karmaşık olduğu genelleştirilmiş bir çerçeve istiyorum. İşte nasıl yapılacağını bilmek istediğim bir şey.

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

Bağlam için, bu makinelerle elde etmek istediğim şey bu:

Temelde durumsal işlevler olan "Akış Transformatörleri" olarak adlandırılan bu şeyleri tasarlamak istiyorum: Jeton tüketiyorlar, iç durumlarını değiştiriyorlar ve bir şeyler üretiyorlar. Özellikle, çıktı bir Boole değeri olduğu bir Stream Transformers sınıfı ile ilgileniyorum; biz bu "monitör" adını vereceğiz.

Şimdi bu nesneler için birleştiriciler tasarlamaya çalışıyorum. Onlardan bazıları:

  • Bir prebirleştirici. monBir monitör olduğunu varsayalım . Ardından, ilk belirteç tüketildikten sonra pre monher zaman Falseüretilen ve daha sonra monönceki belirteç şimdi takılıyormuş gibi davranışı taklit eden bir monitördür . Ben durumunu modellemek isteyeyim pre monile StateWithTriggeryeni devlet orijinal durumuna birlikte bir boolean olduğundan yukarıdaki örnekte.
  • Bir andbirleştirici. Varsayalım m1ve m2monitörler. Ardından, m1 `and` m2jetonu m1'e ve sonra m2'ye besleyen ve Trueher iki cevabın da doğru olup olmadığını üreten bir monitördür . I durumunu model istediğiniz m1 `and` m2ile CombinedStateiki monitör durumu muhafaza edilmesi gerektiği için, yukarıdaki örnekte.

FYI, _innerVal <$> getsadece gets _innerVal(gibi gets f == liftM f getve liftMsadece fmapmonadlar için uzmanlaşmıştır).
chepner

StateT InnerState m Intİlk etapta nereden bir değer elde ediyorsunuz outerStateFoo?
chepner

6
Lens konusunda rahat mısınız? Bu kullanım durumu tam olarak ne için olduğu gibi görünüyor zoom.
Carl

1
@Carl Bazı lensler gördüm ama çok iyi anlamıyorum. Belki bir cevapta zumun nasıl kullanılacağını açıklayabilirsiniz?
Agnishom Chattopadhyay

5
Bir gözlem: Bu kayıt tek bir soru içermez.
Simon Shine

Yanıtlar:


4

İlk soru için Carl belirtildiği gibi, zoomgelen lenstam yapar istediğini. Lensli kodunuz şöyle yazılabilir:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens
import Control.Monad.State.Lazy

newtype InnerState = MkInnerState { _innerVal :: Int }
  deriving (Eq, Ord, Read, Show)

data OuterState = MkOuterState
  { _outerTrigger :: Bool
  , _inner        :: InnerState
  } deriving (Eq, Ord, Read, Show)

makeLenses ''InnerState
makeLenses ''OuterState

innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
  i <- gets _innerVal
  put $ MkInnerState (i + 1)
  return i

outerStateFoo :: Monad m =>  StateT OuterState m Int
outerStateFoo = do
  b <- gets _outerTrigger
  if b
    then zoom inner $ innerStateFoo
    else pure 666

Düzenleme: Biz varken, zaten getiriyorsanız lenso innerStateFoozaman şöyle yazılabilir:

innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = innerVal <<+= 1

5

Bağlam için, bu makinelerle elde etmek istediğim şey bu:

Temelde durumsal işlevler olan "Akış Transformatörleri" olarak adlandırılan bu şeyleri tasarlamak istiyorum: Jeton tüketiyorlar, iç durumlarını değiştiriyorlar ve bir şeyler üretiyorlar. Özellikle, çıktı bir Boole değeri olduğu bir Stream Transformers sınıfı ile ilgileniyorum; biz bu "monitör" adını vereceğiz.

Bence elde etmek istediğiniz şeyin çok fazla makineye ihtiyacı yok.

newtype StreamTransformer input output = StreamTransformer
  { runStreamTransformer :: input -> (output, StreamTransformer input output)
  }

type Monitor input = StreamTransformer input Bool

pre :: Monitor input -> Monitor input
pre st = StreamTransformer $ \i ->
  -- NB: the first output of the stream transformer vanishes.
  -- Is that OK? Maybe this representation doesn't fit the spec?
  let (_, st') = runStreamTransformer st i
  in  (False, st')

and :: Monitor input -> Monitor input -> Monitor input
and left right = StreamTransformer $ \i ->
  let (bleft,  mleft)  = runStreamTransformer left  i
      (bright, mright) = runStreamTransformer right i
  in  (bleft && bright, mleft `and` mright)

Bu StreamTransformerdeğil ille durum bilgisi, ancak durum bilgisi olanları kabul ediyor. Bunları (veya gerçekten de! :) tanımlamak için tip sınıflarına ulaşmak zorunda değilsiniz (ve çoğu zaman IMO olmamalıdır!) Ama bu başka bir konudur).

notStateful :: StreamTransformer input ()
notStateful = StreamTransformer $ \_ -> ((), notStateful)

stateful :: s -> (input -> s -> (output, s)) -> StreamTransformer input output
stateful s k = StreamTransformer $ \input ->
  let (output, s') = k input s
  in  (output, stateful s' k)

alternateBool :: Monitor anything
alternateBool = stateful True $ \_ s -> (s, not s)

Bu çok havalı, teşekkürler! Bu desene bir şey denir mi?
Agnishom Chattopadhyay

3
Ben sadece saf fonksiyonel programlama diyorum! Ama aradığınız cevap bu değil biliyorum :) StreamTransformer aslında bir "Mealy makine" hackage.haskell.org/package/machines-0.7/docs/…
Alexander Vieth

Hayır, ilk çıktı yok etme istediğim gibi değil. İlk çıktıyı ikinci çıktı olarak ertelemek istiyorum .
Agnishom Chattopadhyay

2
Ve böylece her çıkış bir adım geciktirilsin mi? Bu yapılabilir.
Alexander Vieth

1
çok güzel, gönderdiğiniz için teşekkürler! (daha önce kaldırmadan yorum yaptığınız için üzgünüm, Q'yu düzgün okuyun). Bence OP demekti 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')).
Ness Ness
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.