179 votes

Pourquoi les effets secondaires sont modélisés comme des monades en Haskell ?

Quelqu'un pourrait-il donner quelques indications sur pourquoi les calculs impur en Haskell sont modélisés comme des monades ?

Je veux dire monad est juste une interface avec les 4 opérations, ce qui était donc le raisonnement à la modélisation des effets secondaires dedans ?

297voto

KennyTM Points 232647

Supposons qu'une fonction a des effets secondaires. Si nous prenons tous les effets à produire que l'entrée et les paramètres de sortie, alors la fonction est de la pure au monde extérieur.

Donc, pour un impur fonction

f' :: Int -> Int

nous ajoutons de l'Étendue de l'examen

f :: Int -> RealWorld -> (Int, RealWorld)
-- input some states of the whole world,
-- modify the whole world because of the a side effects,
-- then return the new world.

ensuite, f est pure à nouveau. Nous définissons un paramétrées en type de données IO a = RealWorld -> (a, RealWorld), de sorte que nous n'avons pas besoin de type Réaliste, tant de fois

f :: Int -> IO Int

Pour le programmeur, la manipulation d'un RealWorld directement est trop dangereux—en particulier, si un programmeur obtient leurs mains sur une valeur de type RealWorld, ils pourraient essayer de le copier , ce qui est pratiquement impossible. (Pense que d'essayer de copier la totalité du système de fichiers, par exemple. Où placeriez-vous?) Par conséquent, notre définition de IO encapsule les états du monde entier aussi bien.

Ces fonctions impures sont inutiles si on ne peut pas enchaîner les. Envisager

getLine :: IO String               = RealWorld -> (String, RealWorld)
getContents :: String -> IO String = String -> RealWorld -> (String, RealWorld)
putStrLn :: String -> IO ()        = String -> RealWorld -> ((), RealWorld)

Nous voulons obtenir un nom de fichier à partir de la console, lire ce fichier, puis imprimer le contenu. Comment pourrions-nous le faire si nous avons accès au monde réel des etats-unis?

printFile :: RealWorld -> ((), RealWorld)
printFile world0 = let (filename, world1) = getLine world0
                       (contents, world2) = (getContents filename) world1 
                   in  (putStrLn contents) world2 -- results in ((), world3)

Nous voir un modèle ici: les fonctions sont appelées comme ceci:

...
(<result-of-f>, worldY) = f worldX
(<result-of-g>, worldZ) = g <result-of-f> worldY
...

Donc, nous pourrions définir un opérateur ~~~ lier:

(~~~) :: (IO b) -> (b -> IO c) -> IO c

(~~~) ::      (RealWorld -> (b, RealWorld))
      -> (b -> RealWorld -> (c, RealWorld))
      ->       RealWorld -> (c, RealWorld)
(f ~~~ g) worldX = let (resF, worldY) = f worldX in
                        g resF worldY

ensuite, on pourrait simplement écrire

printFile = getLine ~~~ getContents ~~~ putStrLn

sans toucher le monde réel.


Maintenant, supposons que nous voulons rendre le contenu d'un fichier en majuscules. Uppercasing est une pure fonction

upperCase :: String -> String

Mais de le faire dans le monde réel, il doit retourner une IO String. Il est facile de lever une telle fonction:

impureUpperCase :: String -> RealWorld -> (String, RealWorld)
impureUpperCase str world = (upperCase str, world)

ceci peut être généralisé:

impurify :: a -> IO a

impurify :: a -> RealWorld -> (a, RealWorld)
impurify a world = (a, world)

de sorte qu' impureUpperCase = impurify . upperCase, et nous pouvons écrire

printUpperCaseFile = 
    getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn

(Note: Normalement, nous écrivons getLine ~~~ getContents ~~~ (putStrLn . upperCase))


Maintenant, nous allons voir ce que nous avons fait:

  1. Nous avons défini un opérateur (~~~) :: IO b -> (b -> IO c) -> IO c dont les chaînes de deux fonctions impures ensemble
  2. Nous avons défini une fonction impurify :: a -> IO a qui convertit une pure valeur d'impur.

Maintenant, nous faisons l'identification (>>=) = (~~~) et return = impurity, et de voir? Nous avons une monade.


(Pour vérifier si c'est vraiment une monade il y a quelques axiomes doivent être remplies:

(1) return a >>= f = f a

  impurify a               = (\world -> (a, world))
 (impurify a ~~~ f) worldX = let (resF, worldY) = (\world -> (a, world)) worldX 
                             in f resF worldY
                           = let (resF, worldY) =            (a, worldX))       
                             in f resF worldY
                           = f a worldX

(2) f >>= return = f

  (f ~~~ impurify) a worldX = let (resF, worldY) = impuify a worldX 
                              in f resF worldY
                            = let (resF, worldY) = (a, worldX)     
                              in f resF worldY
                            = f a worldX

(3) f >>= (\x -> g x >>= h) = (f >>= g) >>= h

Faire de l'exercice.)

44voto

Conal Points 9874

Quelqu'un pourrait-il donner quelques indications sur les raisons de l'impur calculs en Haskell sont modélisés comme des monades?

Cette question contient une incompréhension généralisée. L'impureté et de la Monade sont indépendants des notions. L'impureté est pas modélisée par une Monade. Au contraire, il ya quelques types de données, comme IO, qui représentent impératif de calcul. Et pour certains de ces types, une infime fraction de leur interface correspond à l'interface modèle appelé "Monade". En outre, il n'existe pas de pure/fonctionnel/denotative explication de l' IO (et il est peu probable, compte tenu de la "sin bin" but de l' IO), mais il est souvent raconté l'histoire à propos de World -> (a, World) étant le sens de l' IO a. Cette histoire ne peut pas honnêtement décrire IOcar IO soutient la simultanéité et non-déterminisme. L'histoire ne fonctionne pas même lorsque, pour des calculs déterministes qui permettent à la mi-calcul de l'interaction avec le monde.

Pour plus d'explication, voir cette réponse.

Edit: Sur la re-lecture de la question, je ne pense pas que ma réponse est tout à fait sur la bonne voie. Les modèles de impératif le calcul ne se révèlent souvent être des monades, tout comme la question a dit. Le demandeur peut pas vraiment supposer que monadness en aucune manière permet la modélisation de l'impératif de calcul.

13voto

Paul Johnson Points 8604

Si je comprends bien, quelqu'un a appelé Eugenio Moggi a d'abord remarqué qu'une obscure construction mathématique appelé une "monade" pourrait être utilisée pour modéliser les effets secondaires dans les langages informatiques, et donc de spécifier la sémantique à l'aide de Lambda calcul. Lorsque Haskell a été développé il y a différentes façons dont impur calculs ont été modélisées (voir Simon Peyton Jones "cilice" papier pour plus de détails), mais quand Phil Wadler introduit monades il est rapidement devenu évident que ce était La Réponse. Et le reste est l'histoire.

9voto

Dario Points 26259

Quelqu'un pourrait-il donner quelques indications sur les raisons de l'impur calculs en Haskell sont modélisés comme des monades?

Eh bien, parce que Haskell est pur. Vous avez besoin d'un concept mathématique de distinguer entre impur calculs et pur sur au niveau du type et de modèle de programme de flux , respectivement.

Cela signifie que vous aurez à la fin avec un certain type IO a que les modèles d'un impur calcul. Ensuite, vous devez savoir les moyens de combiner ces calculs qui s'appliquent dans la séquence (>>=) et le levage d'une valeur (return) sont les plus évidents et les plus de base.

Avec ces deux, vous avez déjà défini une monade (sans même y penser);)

En outre, les monades de fournir de manière très générale et puissant abstractions, de sorte que de nombreux types de flux de contrôle peut être facilement généralisée dans monadique des fonctions comme sequence, liftM ou de syntaxe spéciale, faisant unpureness pas un cas particulier.

Voir les monades dans la programmation fonctionnelle et l'unicité de frappe (la seule alternative que je sais) pour plus d'informations.

4voto

Noah Lavine Points 675

C'est en fait très propre façon de penser, I/O de façon fonctionnelle.

Dans la plupart des langages de programmation, vous n'avez opérations d'entrée/sortie. En Haskell, imaginez l'écriture de code pour ne pas faire les opérations, mais aussi à générer une liste des activités que vous aimeriez faire.

Les monades sont juste assez de syntaxe pour exactement que.

Si vous voulez savoir pourquoi les monades par opposition à quelque chose d'autre, j'imagine que la réponse est qu'ils sont la meilleure façon fonctionnelle pour représenter les I/O que les gens pourraient penser de la date de leur prise de Haskell.

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