Question

J'apprends à connaître les monades et j'ai quelques questions.

C'est où je suis en ce moment. S'il vous plaît, corrigez-moi là où je me trompe.

  • Le symbole >>= est un opérateur infixe. Les opérateurs infixes sont des fonctions qui prennent deux arguments (côté gauche et côté droit) et renvoient une valeur.

  • Le symbole Monad m => m t -> (t -> m u) -> m u s'appelle l'opérateur de liaison et possède une signature m t. Cependant, les types ne semblent pas s'aligner ici. Nous obtenons une valeur de type t et le deuxième argument est une fonction prenant un m. (Je ne vois pas comment relier les points.)

  • Cela doit signifier que la fonction de liaison est en quelque sorte capable de supprimer le <=> du <=> afin d'obtenir le <=> et de le transmettre à la fonction.

Voici mes questions:

  • Possibilité de supprimer le <=> de <=> quelque chose qui n’est possible que dans un tel opérateur de liaison. Cet opérateur de liaison dispose-t-il de privilèges spéciaux ou de quelque chose?

  • Qu'est-ce que cela a à voir avec les changements d'état? Je comprends (je pense) que le but des monades est de «boucler» les effets secondaires de manière à les isoler du reste du programme. Mais quel est le rôle de l’opérateur de liaison dans ce domaine?

Était-ce utile?

La solution

  

La possibilité de supprimer le "M" de "M t" est-elle uniquement possible dans un tel opérateur de liaison. Cet opérateur de liaison possède-t-il des privilèges spéciaux ou autre chose?

La liaison n’est en aucun cas un cas particulier, mais elle sera généralement définie dans le même module que le type de données monads. Par conséquent, il peut connaître (et utiliser) des détails qui ne sont pas exportés par le module. Le cas habituel serait que le module exporte un type de données, mais pas ses constructeurs ni d'autres détails sur la structure interne des types. Ensuite, pour le code qui utilise le module, le fonctionnement interne du type de données est invisible et ce code ne peut pas modifier directement les valeurs de ce type.

Les fonctions opposées à celles définies dans le module, telles que, par exemple, un opérateur de liaison >>=, peuvent accéder à leur guise à partir du module dans lequel elles sont définies. Ces fonctions peuvent donc exécuter des tâches & "external". " les fonctions ne peuvent pas faire.

Un cas particulier est la IO monade, car elle n’est pas définie par un module, mais intégrée au système d’exécution / au compilateur. Ici, le compilateur connaît les détails internes de son implémentation et expose des fonctions telles que celle de <=> de <=>. Les implémentations de ces fonctions sont en effet particulièrement privilégiées puisqu'elles résident & "; En dehors du programme &" ;, mais c'est un cas spécial et ce fait ne doit pas être observé de l'intérieur de Haskell.

  

Qu'est-ce que cela a à voir avec les changements d'état? Je comprends (je pense) que le but des monades est de «boucler» les effets secondaires de manière à les isoler du reste du programme. Mais quel est le rôle de l’opérateur de liaison dans ce domaine?

Cela n’a pas vraiment besoin d’être lié aux changements d’état, c’est juste un problème qui peut être traité avec des requêtes. La <=> monade permet d’exécuter des opérations d’E / S dans un certain ordre, mais les monades ne sont généralement que des moyens de combiner des fonctions.

Généralement, une monade (en particulier sa fonction de liaison) définit une manière dont certaines fonctions doivent être composées ensemble pour de plus grandes fonctions. Cette méthode de combinaison de fonctions est résumée dans la monade. Comment cette combinaison fonctionne-t-elle ou pourquoi vous souhaitez combiner des fonctions de cette manière n'a pas d'importance? Une monade spécifie simplement une manière de combiner certaines fonctions d'une certaine manière. (Voir aussi ce & Quot; Monads pour C # les programmeurs & "; répondre où je répète en gros cela quelques fois avec des exemples.)

Autres conseils

  

est la possibilité de supprimer le "M" de "M t", ce qui n’est possible que dans un tel opérateur de liaison.

Eh bien, il est certainement possible à l'intérieur de l'opérateur de liaison, comme son type l'indique:

(>>=) :: m a -> (a -> m b) -> m b

La fonction "exécuter" de votre monade peut généralement le faire aussi (pour renvoyer une valeur pure de votre calcul).

  

L’objectif des monades est de «boucler» les effets secondaires de manière à les isoler du reste du programme

Hmm. Non, les monades permettent de modéliser les notions de calcul. Les calculs à effets secondaires ne sont que l'une de ces notions: état, retour en arrière, continuations, concurrence, transactions simultanées, transactions, résultats facultatifs, résultats aléatoires, état réversible, non déterminisme ... le tout peut être décrit comme une monade

Vous parlez de la monade IO, je suppose. C'est une monade un peu étrange - elle génère des séquences de modifications abstraites de l'état du monde, qui sont ensuite évaluées par le moteur d'exécution. Bind nous permet simplement de séquencer les choses dans le bon ordre dans la monade d’IO - et le compilateur traduira alors toutes ces actions de modification du monde séquencées en un code impératif modifiant cet état de la machine.

C'est très spécifique à la monade IO, pas aux monades en général.

Ce qui suit est la définition de la classe de type Monad.

class  Monad m  where

    (>>=)       :: forall a b. m a -> (a -> m b) -> m b
    (>>)        :: forall a b. m a -> m b -> m b
    return      :: a -> m a
    fail        :: String -> m a

    m >> k      = m >>= \_ -> k
    fail s      = error s

Chaque instance de type de classe de type >>= définit sa propre Maybe fonction. Voici un exemple tiré de l'instance de type data Maybe a:

instance  Monad Maybe  where

    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

    (Just _) >>  k      = k
    Nothing  >>  _      = Nothing

    return              = Just
    fail _              = Nothing

Comme on peut le constater, la Nothing version de Just a est spécialement définie pour comprendre l'instance de type a, et parce qu'elle est définie dans un emplacement disposant d'un accès légal aux constructeurs de données Maybe a <=> et <=>, la <=> version de <=> peut décompresser les <=> dans <=> et les transmettre.

Pour travailler à travers un exemple, nous pourrions prendre:

x :: Maybe Integer
x = do a <- Just 5
       b <- Just (a + 1)
       return b

Sucré, la notation do devient:

x :: Maybe Integer
x = Just 5        >>= \a ->
    Just (a + 1)  >>= \b ->
    Just b

Ce qui se traduit par:

  =                  (\a ->
    Just (a + 1)  >>= \b ->
    Just b) 5

  = Just (5 + 1)  >>= \b ->
    Just b

  =                  (\b ->
    Just b) (5 + 1)

  = Just (5 + 1)

  = Just 6

Les types s’alignent, assez curieusement. Voici comment.

Rappelez-vous qu’une monade est aussi un foncteur. La fonction suivante est définie pour tous les foncteurs:

fmap :: (Functor f) => (a -> b) -> f a -> f b

Maintenant, la question: ces types s’alignent-ils vraiment? Hé bien oui. Avec une fonction de a à b, si nous avons un environnement f dans lequel m est disponible, nous avons un environnement m a dans lequel m (m a) est disponible.

Par analogie avec le syllogisme:

(Functor Socrates) => (Man -> Mortal) -> Socrates Man -> Socrates Mortal

Maintenant, comme vous le savez, une monade est un foncteur équipé de bind et de return:

return :: (Monad m) => a -> m a
(=<<) :: (Monad m) => (a -> m b) -> m a -> m b

Vous n’êtes peut-être pas au courant, c’est un foncteur équipé de return et join:

join :: (Monad m) => m (m a) -> m a

Voyez comment nous éliminons un (=<<). Avec une monade (a -> m b), vous ne pouvez pas toujours aller de fmap à m a -> m (m b), mais vous pouvez toujours aller de a -> m b à m (m b).

Examinons maintenant le premier argument de join. C'est une fonction de type <=>. Que se passe-t-il lorsque vous transmettez cette fonction à <=>? Vous obtenez <=>. Ainsi, & Quot; mapping & Quot; sur un <=> avec une fonction <=> vous donne <=>. Notez que cela correspond exactement au type de l'argument de <=>. Ce n'est pas une coïncidence. Une implémentation raisonnable de & Quot; bind & Quot; ressemble à ceci:

(>>=) :: m a -> (a -> m b) -> m b
x >>= f = join (fmap f x)

En fait, lier et rejoindre peuvent être définis l'un par rapport à l'autre:

join = (>>= id)

Je vous recommande TRES BEAUCOUP de lire ( http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html ). Cela explique parfaitement pourquoi les monades existent.

  

Je comprends (je pense) que l'objectif des monades est de «masquer» les effets secondaires de manière à les isoler du reste du programme.

C'est en fait un peu plus subtile que cela. Les monades nous permettent de modéliser le séquençage de manière très générale. Souvent, lorsque vous parlez à un expert du domaine, vous le trouvez en train de dire quelque chose comme & "; Nous essayons d’abord X. Ensuite, nous essayons Y, et si cela ne fonctionne pas, alors nous essayons Z &"; Lorsque vous implémentez quelque chose comme ça dans un langage conventionnel, vous trouvez que cela ne convient pas, vous devez donc écrire beaucoup de code supplémentaire pour couvrir tout ce que l'expert du domaine entend par le mot & "Puis &" ;.

Dans Haskell, vous pouvez implémenter cela en tant que monade avec le & "puis &"; traduit dans l'opérateur de liaison. Ainsi, par exemple, j’ai écrit un jour un programme dans lequel un élément devait être attribué à partir de pools selon certaines règles. Pour le cas 1, vous l'avez pris dans le pool X. Si c'était vide, vous êtes passé au pool Y. Dans le cas 2, vous deviez le prendre directement du pool Y. Et ainsi de suite pour une douzaine de cas, y compris dans certains cas le moins récemment utilisé du pool X ou Y. J'ai écrit une monade personnalisée spécialement pour ce travail afin de pouvoir écrire:

case c of
   1: do {try poolX; try poolY}
   2: try poolY
   3: try $ lru [poolX, poolY]

Cela a très bien fonctionné.

Bien sûr, cela inclut les modèles classiques de séquençage. La monade IO est le modèle de tous les autres langages de programmation; En Haskell, c’est un choix explicite plutôt qu’une partie de l’environnement. La monade ST vous donne la mutation de mémoire d'IO, mais sans l'entrée ni la sortie réelle. D'autre part, la monade d'état vous permet de restreindre votre état à une seule valeur d'un type nommé.

Pour quelque chose de vraiment difficile, voir cet article de blog sur une monade en arrière. L'état se propage dans le sens opposé à l'exécution & "; Exécution &". Si vous considérez cela comme une monade d'état exécutant une instruction suivie d'une autre, un & "Mettre &"; enverra la valeur d'état en arrière dans le temps à tous les & précédents; obtiendrons & Ce qui se produit réellement , c’est qu’une fonction mutuellement récursive est configurée et ne se termine que s’il n’ya pas de paradoxe. Je ne sais pas trop où utiliser une telle monade, mais cela montre bien que les monades sont des modèles de calcul.

Si vous n'êtes pas prêt pour cela, pensez simplement à bind comme un point-virgule surchargeable. Cela vous fait un long chemin.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top