trayectoria tipos termodinamico termodinamicas termodinamica son sistemas sistema las funciones estado dinamicos cuales haskell state-monad

haskell - tipos - ¿Cómo evito referirme a todas las variables de estado cuando actualizo solo unas pocas?



variables de trayectoria (3)

El combinador de zoom lens eleva un cálculo en una mónada de State a un cálculo que se ejecuta en una mónada de State "más grande".

zoom :: Lens'' s t -> State t a -> State s a

Entonces, dado un estado "grande":

data Big = Big { _big1 :: Medium, _big2 :: Medium } data Medium = Medium { _medium1 :: Small, _medium2 :: Small } data Small = Small { _small :: Int } makeLenses ''''Big makeLenses ''''Medium makeLenses ''''Small

puede "acercar" una parte del estado:

incr :: State Int () incr = id += 1 incrSmall :: State Big () incrSmall = zoom (big2.medium1.small) incr

Por supuesto, esto funcionará tanto en tuplas grandes como en discos, utilizando los Control.Lens.Tuple incorporados en la lens .

La firma de tipo real de zoom es más general que la simple citada anteriormente. Utiliza MonadState restricciones de MonadState para trabajar bajo una pila de transformadores de mónada, en lugar de hacerlo específicamente en el State .

Un lenguaje que uso para componer un par de procedimientos (con memoria) es el siguiente:

p1 :: State (Int, String) () p1 = do (a, b) <- get ... do something ... put (a'', b) p2 :: State (Int, String) () p2 = do (a, b) <- get ... do something else ... put (a, b'') main = do ... initializing a0 b0 ... print . flip evalState (a0, b0) . sequence $ replicate 10 p1 ++ repeat p2

Sin embargo, a medida que crece el número de variables de estado, esto se vuelve rápidamente más detallado de lo necesario:

p1 :: State (Int, String, Bool, Int, String, Bool) () p1 = do (a, b, c, d, e, f) <- get ... do something ... put (a, b, c'', d, e, f'') p2 :: State (Int, String, Bool, Int, String, Bool) () p2 = do (a, b, c, d, e, f) <- get ... do something ... put (a'', b'', c, d, e, f) main = do print . flip evalState (a0, b0, c0, d0, e0, f0) . sequence $ replicate 10 p1 ++ repeat p2

Como me preguntaba, ¿hay alguna forma de actualizar solo algunas variables de estado sin tener que referirse a todas las que no se utilizan? Estaba pensando en algo como IORef pero para State (de hecho, hay un paquete stateref ), pero no estoy seguro si ya hay algunos modismos comunes que otras personas han estado usando.


Esta es una buena situación para usar registros, con las funciones de gets y modify para manipular subpartes del estado:

data Env = Env { envNumber :: Int , envText :: String } p1 :: State Env () p1 = do a <- gets envNumber -- ... modify $ /r -> r { envNumber = a'' } p2 :: State Env () p2 = do b <- gets envText -- ... modify $ /r -> r { envText = b'' }

gets convierte una función getter pura en una acción de estado:

gets :: (s -> a) -> State s a envNumber :: Env -> Int gets envNumber :: State Env Int

Y modify convierte una función de actualización pura en una acción de estado:

modify :: (s -> s) -> State s () (/r -> r { envText = b'' }) :: Env -> Env modify (/r -> ...) :: State Env ()


Esto parece ser un trabajo para lenses . Especialmente el módulo Control.Lens.Tuple junto con .= Y use :

p1 = do a <- use _1 -- do something -- _1 .= a''

Sin embargo, generalmente es mejor si le da a los nombres propios de su estado, por ejemplo:

{-# LANGUAGE TemplateHaskell #- data Record = MkRecord { _age :: Int , _name :: String , _programmer :: Bool } deriving (Show, Eq) makeLenses ''''Record

De esa manera, tienes mejores nombres para tu campo:

p1 = do a <- use age -- do something -- age .= a''

Tenga en cuenta que esto todavía le ayuda si no desea usar lentes, ya que puede usar la sintaxis de registro para actualizar sus datos:

p1 = do r <- get let a = _age r --- do something put $ r{_age = a''}