4 votes

La monade d'État est-elle applicable à une simple "boucle" récursive ?

J'apprends Haskell à partir de "LYAH" et je suis arrivé à la monade State. Comme exercice, je travaille sur l'implémentation d'un simple "virtual cpu". La monade d'état semble être une bonne solution pour cela mais je n'arrive pas à comprendre comment l'utiliser. Voici un exemple très dépouillé de ce que j'ai pour l'instant (sans la monade d'état) :

data Instruction = Incr | Decr | Halt

data Computer = Computer { program :: [Instruction],
                           acc :: Int,
                           pc :: Int,
                           halted :: Bool
                         }

main = do
  let comp = Computer { program = [Incr, Decr, Incr, Incr, Halt]
                      , acc = 0
                      , pc = 0
                      , halted = False
                      }
  execute comp

execute :: Computer -> IO ()
execute comp = do
  let (output, comp') = step comp
  putStrLn output
  case halted comp' of
    False -> execute comp'
    True -> return ()

step :: Computer -> (String, Computer)
step comp = let inst = program comp !! pc comp
            in case inst of
                 Decr -> let comp' = comp{ acc = acc comp - 1,
                                           pc = pc comp + 1 }
                             output = "Increment:" ++
                                      "   A = " ++ (show $ acc comp') ++
                                      "  PC = " ++  (show $ pc comp')
                         in (output, comp')
                 Incr -> let comp' = comp{ acc = acc comp + 1,
                                           pc = pc comp + 1 }
                             output = "Decrement:" ++
                                      "   A = " ++ (show $ acc comp') ++
                                      "  PC = " ++  (show $ pc comp')
                         in (output, comp')
                 Halt -> let comp' = comp{halted = True}
                             output = "HALT"
                         in (output, comp')

Mon step ressemble à la monade d'état mais je ne vois pas comment l'utiliser ici. Serait-elle applicable ? Est-ce que cela simplifierait ce code, ou est-ce que cet exemple est meilleur tel qu'il est ?

7voto

leftaroundabout Points 23679

Absolument. step peuvent être traduits de manière assez mécanique :

import Control.Monad.Trans.State

step :: State Computer String
step = do
   comp <- get
   case program comp !! pc comp of
    Decr -> do 
      let comp' = comp { acc = acc comp - 1
                       , pc = pc comp + 1 }
      put comp'
      return $ "Increment:"
                  ++ "   A = " ++ (show $ acc comp')
                  ++ "  PC = " ++  (show $ pc comp')
    Incr -> do
      let comp' = comp { acc = acc comp + 1
                       , pc = pc comp + 1 }
      put comp'
      return $ "Decrement:"
              ++ "   A = " ++ (show $ acc comp')
              ++ "  PC = " ++  (show $ pc comp')
    Halt -> do
      put $ comp{halted = True}
      return "HALT"

(Ces let comp' = ... sont encore un peu gênants. Il pourrait être rendu plus impératif en utilisant les modificateurs de champ d'état de les lens bibliothèque .)

Vous remarquerez peut-être que State n'est en fait qu'une spécialisation de la fonction StateT et aucune des fonctions que j'ai utilisées n'est spécifique à cela. En d'autres termes, vous pouvez directement généraliser la signature à

step :: Monad m => StateT Computer m String

Cela s'avère très utile pour execute car là, on intercale les modifications de l'état avec les modifications de l'état. IO effets secondaires - c'est exactement le genre de choses auxquelles sert un transformateur.

import Control.Monad.IO.Class

execute :: StateT Computer IO ()
execute = do
  output <- step
  liftIO $ putStrLn output
  comp <- get
  case halted comp of
    False -> execute
    True -> return ()

Enfin, en main il suffit de

main = do
  let comp = ...
  evalStatT execute comp

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X