56 votes

E / S pré-monadiques Haskell

Je me demande comment les E / S ont été effectuées à Haskell à l'époque où la monade IO n'était toujours pas inventée. Tout le monde connaît un exemple.

Edit: les E / S peuvent-elles être effectuées sans la IO Monad dans Haskell moderne? Je préfère un exemple qui fonctionne avec le GHC moderne.

65voto

sepp2k Points 157757

Avant le IO monade a été introduit, main a une fonction de type [Response] -> [Request]. Un Request représenterait une I/O action, comme l'écriture d'un canal ou d'un fichier, ou la lecture de l'entrée ou de la lecture des variables d'environnement etc.. Un Response serait le résultat d'une telle action. Par exemple, si vous avez effectué une ReadChan ou ReadFile de la demande, le correspondant Request serait Str strstr serait un String contenant de la lecture de l'entrée. Lors de l'exécution d'un AppendChan, AppendFile ou WriteFile requête, la réponse serait tout simplement Success. (En supposant que, dans tous les cas, que l'action était en fait un succès, bien sûr).

Ainsi, un programme Haskell fonctionne par la constitution d'une liste d' Request valeurs et de la lecture les réponses correspondantes à partir de la liste donnée à l' main. Par exemple, un programme pour lire un certain nombre de l'utilisateur pourrait ressembler à ceci (en laissant toute erreur de manipulation pour des raisons de simplicité):

main :: [Response] -> [Request]
main responses =
  [
    AppendChan "stdout" "Please enter a Number\n",
    ReadChan "stdin",
    AppendChan "stdout" . show $ enteredNumber * 2
  ]
  where (Str input) = responses !! 1
        firstLine = head . lines $ input
        enteredNumber = read firstLine 

Comme Stephen Tetley déjà souligné dans un commentaire, une description détaillée de ce modèle est donnée dans le chapitre 7 de la 1.2 Haskell Rapport.


Puis-je/O en être faite sans l'IO Monade moderne Haskell?

Pas de. Haskell ne supporte plus l' Response/Request façon de faire des IO directement et le type d' main est désormais IO (), de sorte que vous ne pouvez pas écrire un programme Haskell qui n'implique pas de IO , et même si tu le pouvais, tu serais encore n'ont pas d'autres façon de faire des I/O.

Ce que vous pouvez faire, cependant, est d'écrire une fonction qui prend un vieux style de la fonction principale et la transforme en un IO action. Vous pouvez ensuite écrire tout en utilisant le style ancien et alors qu'à utiliser IO main où vous pouvez simplement appeler la fonction de conversion sur votre véritable fonction principale. Le faire serait presque certainement être de plus en plus lourde qu'à l'aide de l' IO monade (et ne pas confondre l'enfer hors de toute moderne Haskeller la lecture de votre code), donc je ne le recommanderais pas. Toutefois, il est possible. Une telle fonction de conversion pourrait ressembler à ceci:

import System.IO.Unsafe

-- Since the Request and Response types no longer exist, we have to redefine
-- them here ourselves. To support more I/O operations, we'd need to expand
-- these types

data Request =
    ReadChan String
  | AppendChan String String

data Response =
    Success
  | Str String
  deriving Show

-- Execute a request using the IO monad and return the corresponding Response.
executeRequest :: Request -> IO Response
executeRequest (AppendChan "stdout" message) = do
  putStr message
  return Success
executeRequest (AppendChan chan _) =
  error ("Output channel " ++ chan ++ " not supported")
executeRequest (ReadChan "stdin") = do
  input <- getContents
  return $ Str input
executeRequest (ReadChan chan) =
  error ("Input channel " ++ chan ++ " not supported")

-- Take an old style main function and turn it into an IO action
executeOldStyleMain :: ([Response] -> [Request]) -> IO ()
executeOldStyleMain oldStyleMain = do
  -- I'm really sorry for this.
  -- I don't think it is possible to write this function without unsafePerformIO
  let responses = map (unsafePerformIO . executeRequest) . oldStyleMain $ responses
  -- Make sure that all responses are evaluated (so that the I/O actually takes
  -- place) and then return ()
  foldr seq (return ()) responses

Vous pouvez ensuite utiliser cette fonction comme ceci:

-- In an old-style Haskell application to double a number, this would be the
-- main function
doubleUserInput :: [Response] -> [Request]
doubleUserInput responses =
  [
    AppendChan "stdout" "Please enter a Number\n",
    ReadChan "stdin",
    AppendChan "stdout" . show $ enteredNumber * 2
  ]
  where (Str input) = responses !! 1
        firstLine = head . lines $ input
        enteredNumber = read firstLine 

main :: IO ()
main = executeOldStyleMain doubleUserInput

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