J'ai fait quelques recherches sur stackoverflow pour trouver une solution viable au problème courant du maintien de différents états d'une variable globale.
J'ai trouvé este une question élaborée qui répond à une préoccupation similaire. Elle soulève un problème important, celui des variables globales de type "godlike", ce qui est un anti-modèle en Haskell. Je comprends parfaitement que ma situation est similaire et que j'essaie d'introduire cet anti-modèle, mais je n'aime pas vraiment la réponse. Il semble que Netwire
est exagéré pour la tâche qui m'incombe, il pourrait être réalisé de manière beaucoup plus simple et élégante.
J'ai également trouvé celui-ci Mais les questions et les réponses portent sur des préoccupations et des approches plus générales, alors que j'ai un problème concret et, je l'espère, une solution concrète. Ce que je souhaite également (et que je n'ai pas trouvé dans les questions précédentes), c'est faire un pas qualitatif dans la compréhension du maintien des états des variables à travers un exemple simple.
Dans le code ci-dessous, j'essaie de mettre à jour l'état de la variable Godlike à partir de deux endroits différents en exécutant :load
y :new
mais, manifestement, cela ne fonctionne pas.
Ma question est la suivante : comment modifier le code suivant afin de tenir compte de la possibilité de modifier la valeur de la variable globale de manière fonctionnelle ? Dois-je jeter tout le code parce qu'il représente une approche de type impératif et le remplacer par un code totalement nouveau ? parseInput
qui suit les règles du monde fonctionnel ? Dois-je remplacer la variable globale par quelque chose d'autre ? Je suppose que je pourrais utiliser IORef
D'une manière ou d'une autre, cela semble approprié. Ou bien ST Monad
comme cette question/réponse recommandent.
Quelle serait la mesure la plus simple et la plus directe pour résoudre ce problème sans excès ? Je comprends que je puisse avoir besoin de mieux saisir la notion de Monades (State Monad en particulier) et je suis prêt à apprendre comment elles pourraient m'aider à résoudre ce problème particulier. Mais les articles que j'ai lus jusqu'à présent ( este y este ), n'a pas été d'un grand secours. Je suppose que la monade d'état n'est pas vraiment appropriée parce que mon exemple n'a pas de valeur de retour, seulement un état mis à jour. Si je me trompe, pourriez-vous m'expliquer comment et quels liens manquants m'aideraient à mieux comprendre les états en Haskell ?
{-# LANGUAGE QuasiQuotes #-}
import Text.Regex.PCRE
import System.Console.Haskeline
import TH (litFile)
import System.FilePath
import System.IO
import Control.Monad
import Control.Monad.IO.Class
import Data.List
mydata :: [Int]
mydata = [0]
saveDataToFile :: [Int] -> IO ()
saveDataToFile mydata = withFile "data.txt" WriteMode $ \h -> System.IO.hPutStr h (unwords $ map show mydata)
loadDataFromFile :: [Int]
loadDataFromFile = map read . words $ [litFile|data.txt|]
help :: InputT IO ()
help = liftIO $ mapM_ putStrLn
[ ""
, ":help - this help"
, ":q - quit"
, ":commands - list available commands"
, ""
]
commands :: InputT IO ()
commands = liftIO $ mapM_ putStrLn
[ ""
, ":show - display data"
, ":save - save results to file"
, ":load - loads data from file"
, ":new - generate new element "
, ""
]
parseInput :: String -> InputT IO ()
parseInput inp
| inp =~ "^\\:q" = return ()
| inp =~ "^\\:he" = help >> mainLoop
| inp =~ "^\\:commands" = commands >> mainLoop
| inp =~ "^\\:show" = do
liftIO $ putStrLn $ unwords $ map show mydata
mainLoop
| inp =~ "^\\:save" = do
liftIO $ saveDataToFile mydata
mainLoop
| inp =~ "^\\:load" = do
let mydata = loadDataFromFile -- <-- should update mydata
mainLoop
| inp =~ "^\\:new" = do
let mydata = mydata ++ [last mydata + 1] -- <-- should update mydata
mainLoop
| inp =~ ":" = do
outputStrLn $ "\nNo command \"" ++ inp ++ "\"\n"
mainLoop
| otherwise = handleInput inp
handleInput :: String -> InputT IO ()
handleInput inp = mainLoop
mainLoop :: InputT IO ()
mainLoop = do
inp <- getInputLine "% "
maybe (return ()) (parseInput) inp
greet :: IO ()
greet = mapM_ putStrLn
[ ""
, " MyProgram"
, "=============================="
, "For help type \":help\""
, ""
]
main :: IO ()
main = do
greet
runInputT defaultSettings (mainLoop)
PS. J'utilise les définitions Template Haskell (module TH) de cette réponse .