881 votes

Monade en anglais ordinaire? (Pour le programmeur POO sans arrière-plan FP)

En termes qu'un programmeur POO comprendrait (sans arrière-plan de programmation fonctionnelle), qu'est-ce qu'une monade?

Quel problème résout-il et quels sont les endroits les plus communs où il est utilisé?

MODIFIER:

Pour clarifier le type de compréhension que je cherchais, disons que vous étiez en train de convertir une application de PF qui avait des monades en une application POO. Que feriez-vous pour transférer les responsabilités des monades à l'application POO?

850voto

Eric Lippert Points 300275

Mise à JOUR: Cette question a fait l'objet d'une immense longue série de blog, vous pouvez lire les Monades -- merci pour la grande question!

Dans les termes d'une OOP programmeur de le comprendre (sans aucune programmation fonctionnelle en arrière-plan), ce qui est une monade?

Une monade est un "amplificateur" de types qui obéit à certaines règles et qui a certaines opérations prévues.

Tout d'abord, qu'est ce qu'un "amplificateur de types"? Je veux dire par là un système qui vous permet de prendre un type et de le transformer en un plus d'un type spécial. Par exemple, en C# envisager Nullable<T>. C'est un amplificateur de types. Il vous permet de prendre un type, disons int, et d'ajouter une nouvelle fonctionnalité à ce type, à savoir, que maintenant, il peut être null quand il ne pouvait pas avant.

Comme deuxième exemple, envisager IEnumerable<T>. C'est un amplificateur de types. Il vous permet de prendre un type, disons, string, et d'ajouter une nouvelle fonctionnalité à ce type, à savoir, que vous pouvez désormais effectuer une séquence de chaînes de n'importe quel nombre de cordes simples.

Quels sont les "certaines règles"? Brièvement, qu'il y est un moyen judicieux pour les fonctions sur le type sous-jacent à travailler sur le amplifié type tels qu'ils suivent les règles normales de la fonctionnelle de la composition. Par exemple, si vous avez une fonction sur les entiers, dire

int M(int x) { return x + N(x * 2); }

ensuite, la fonction correspondante sur Nullable<int> peut faire tous les opérateurs et les appels à y travailler ensemble "de la même manière qu'ils le faisaient avant.

(Qui est extrêmement vague et imprécise; vous avez demandé une explication qui n'a pas supposer quoi que ce soit à propos de la connaissance de la fonctionnelle de composition).

Quels sont les "opérations"?

  1. Qu'il y est une façon de prendre une valeur d'une amplification de type et de le transformer en une valeur de l'amplification de type.
  2. Qu'il existe un moyen de transformer les opérations sur l'amplification de type opérations sur le amplifié type qui obéit aux règles de la fonctionnelle de la composition mentionné avant
  3. Qu'il est généralement un moyen pour obtenir l'amplification de type de retour de la amplifié type. (Ce dernier point n'est pas strictement nécessaire pour une monade, mais c'est souvent le cas qu'une telle opération existe.)

Encore une fois, prendre en Nullable<T> comme un exemple. Vous pouvez transformer un int en Nullable<int> avec le constructeur. Le compilateur C# prend soin de la plupart des nullable "lifting" pour vous, mais si ce n'est pas le cas, la levée de la transformation est simple: une opération, dire,

int M(int x) { whatever }

est transformé en

Nullable<int> M(Nullable<int> x) 
{ 
    if (x == null) 
        return null; 
    else 
        return new Nullable<int>(whatever);
}

Et en tournant nullable int de retour dans un int est fait avec la Valeur de la propriété.

C'est la transformation de la fonction qui est la clé de bits. Remarquez comment le réel de la sémantique de la nullable opération -- qu'une opération sur une valeur null propage l'null -- est capturé dans la transformation. On peut la généraliser. Supposons que vous disposez d'une fonction de type int int, comme l'origine, notre M. Vous pouvez facilement faire une fonction qui prend un entier et retourne un Nullable<int> parce que vous pouvez simplement exécuter le résultat par le biais de la nullable constructeur. Maintenant, supposons que vous avez cet accroissement de la méthode de l'ordre:

Nullable<T> Bind<T>(Nullable<T> amplified, Func<T, Nullable<T>> func)
{
    if (amplified == null) 
        return null;
    else
        return func(amplified.Value);
}

Voir ce que vous pouvez faire avec cela? Toute méthode qui prend un entier et retourne un int, ou prend un entier et retourne un nullable int pouvez maintenant avoir la nullable de la sémantique appliquée.

En outre: supposons que vous avez deux méthodes

Nullable<int> X(int q) { ... }
Nullable<int> Y(int r) { ... }

et vous souhaitez composer eux:

Nullable<int> Z(int s) { return X(Y(s)); }

C'est, Z est la composition de X et Y. Mais vous ne pouvez pas le faire parce que X prend un int, et Y retourne nullable int. Mais puisque vous avez le "bind" de l'opération, vous pouvez faire ce travail:

Nullable<int> Z(int s) { return Bind(Y(s), X); }

L'opération de liaison sur une monade est ce qui rend la composition de fonctions sur amplifiée types de travail. Les "règles" je handwaved de parler ci-dessus sont que la monade conserve les règles de la fonction normale de la composition; que la composition de l'identité des fonctions de résultats dans la fonction d'origine, que la composition est associative, et ainsi de suite.

En C#, "Bind" est appelé "SelectMany". Jetez un oeil à la façon dont il fonctionne sur la séquence de la monade. Nous avons besoin de trois choses: la transformation d'une valeur dans une séquence, transformer une séquence en une valeur, et de lier les opérations sur les séquences. Ces opérations sont:

IEnumerable<T> MakeSequence<T>(T item)
{
    yield return item;
}
T Single<T>(IEnumerable<T> sequence)
{
    // let's just take the first one
    foreach(T item in sequence) return item; 
}
IEnumerable<T> SelectMany<T>(IEnumerable<T> seq, Func<T, IEnumerable<T>> func)
{
    foreach(T item in seq)
        foreach(T result in func(item))
            yield return result;            
}

Le nullable monade règle était "pour combiner les deux fonctions qui produisent nullable ensemble, vérifier pour voir si l'intérieur de l'un des résultats null; si ce n'est, à produire de la valeur null si elle n'est pas, alors appelez de l'extérieur du résultat". C'est le désiré de la sémantique des valeurs null. La séquence de l'errance de la règle est de "combiner les deux fonctions qui produisent des séquences, appliquer l'extérieur de la fonction à chaque élément produit par la fonction interne, et puis concaténer toutes les séquences obtenues ensemble". Les fondamentaux de la sémantique des monades sont capturés dans le Bind/SelectMany méthodes; c'est la méthode qui vous dit que la monade vraiment les moyens.

Nous pouvons faire encore mieux. Supposons que vous avez une des séquences d'entiers, et une méthode qui prend ints et les résultats dans des séquences de chaînes de caractères. On peut généraliser l'opération de liaison pour permettre la composition de fonctions et différents retour amplifié types, aussi longtemps que les entrées de un match les sorties de l'autre:

IEnumerable<U> SelectMany<T,U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> func)
{
    foreach(T item in seq)
        foreach(U result in func(item))
            yield return result;            
}

Alors maintenant, nous pouvons dire "amplification de ce groupe de personne entiers dans une séquence d'entiers. Transformer ce nombre entier en un bouquet de chaînes, amplifié à une séquence de chaînes de caractères. Maintenant, peut mettre à la fois l'ensemble des opérations: compléter cet ensemble d'entiers dans la concaténation de toutes les séquences de chaînes." Les monades vous permettent de composer votre amplifications.

Quel problème à résoudre et quels sont les endroits les plus communs, il est utilisé?

C'est un peu comme demander "quels sont les problèmes liés à un singleton résoudre?", mais je vais donner un coup de feu.

Les monades sont généralement utilisés pour résoudre des problèmes tels que:

  • J'ai besoin de faire de nouvelles capacités pour ce type et encore combiner les anciennes fonctions de ce type à utiliser les nouvelles fonctionnalités.
  • J'ai besoin de saisir un tas d'opérations sur les types et les représentent ces opérations comme composable objets, la construction de plus en plus grandes compositions jusqu'à ce que j'ai juste le droit de la série d'opérations a représenté, et puis j'ai besoin pour commencer à obtenir des résultats de la chose
  • J'ai besoin de représenter côté-effectuer les opérations proprement dans une langue qui déteste les effets secondaires

(À noter que ce sont essentiellement trois façons de dire la même chose.)

C# utilise les monades dans sa conception. Comme déjà mentionné, la nullable modèle est très proche de la "monade". LINQ est entièrement construit à partir de monades; le "SelectMany" la méthode est quoi la sémantique travail de composition de l'exploitation. (Erik Meijer est friand de souligner que chaque LINQ fonction pourrait effectivement être mis en œuvre par SelectMany; tout le reste est juste une commodité.)

Afin de clarifier la nature de la compréhension que je cherchais, disons que vous avez été la conversion d'un FP demande monades en programmation orientée objet application. Que feriez-vous pour port les responsabilités des monades dans la programmation orientée objet application?

La plupart de la programmation orientée objet les langues n'ont pas assez riche système de type pour représenter la monade modèle lui-même directement; vous avez besoin d'un type de système qui prend en charge les types qui sont plus élevés types de types génériques. Je ne voudrais pas essayer de le faire. Plutôt, je voudrais mettre en œuvre les types génériques qui représentent chaque monade, et de mettre en œuvre des méthodes qui représentent les trois opérations dont vous avez besoin: tournage d'une valeur dans un amplifié valeur, la transformation d'un amplifié valeur en une valeur, et la transformation d'une fonction d'amplification des valeurs dans une fonction amplifié valeurs.

Un bon endroit pour commencer est de savoir comment nous avons mis en place LINQ en C#. L'étude de la SelectMany méthode; elle est la clé pour comprendre comment la séquence monade travaille en C#. C'est une méthode très simple, mais très puissant!

Pour un plus en profondeur et, théoriquement, son explication de monades en C#, je vous recommande vivement mon collègue de Wes Dyer article sur le sujet. Cet article est ce qui explique les monades à moi quand ils ont enfin "cliqué" pour moi.

Les Merveilles de Monades

104voto

JacquesB Points 19878

Je dirais que le plus proche OO analogie aux monades est le "modèle de commande".

Dans le modèle de commande vous envelopper ordinaires de la déclaration ou de l'expression d'une commande de l'objet. L'objet de commande exposer une exécution de la méthode qui exécute le enveloppés déclaration. Afin de déclaration sont transformés en objets de première classe qui peut passés et exécutés. Les commandes peuvent être composées de sorte que vous pouvez créer un programme-objet par le chaînage et l'imbrication de commande des objets.

Les commandes sont exécutées par un objet distinct, l' invocateur. Les avantages de l'utilisation du modèle de commande (plutôt que de simplement exécuter une série d'ordinaire consolidés) est que les différentes invocateurs peuvent appliquer une logique différente de la façon dont les commandes doivent être exécutées.

Le modèle de commande peut être utilisé pour ajouter (ou supprimer) les fonctionnalités de la langue qui n'est pas pris en charge par la langue d'accueil. Par exemple, dans un hypothétique langage OO, sans exceptions, vous pouvez ajouter de l'exception de la sémantique par l'exposition "essayer" et "jeter" des méthodes pour les commandes. Lorsqu'une commande d'appels, le jet de l'invocateur revient par le biais de la liste (ou un arbre) de commandes jusqu'à la dernière "essayer" appel. Inversement, vous pouvez supprimer exception sémantique à partir d'une langue (si vous croyez que les exceptions sont mauvais) par attraper toutes les exceptions levées par chaque individu commandes, et de les transformer en des codes d'erreur qui sont ensuite transmis à la prochaine commande.

Encore plus de fantaisie sémantique d'exécution des transactions, de non-déterministe de l'exécution ou de poursuites ne peuvent être mis en œuvre comme cela dans une langue qui ne prend pas en charge en mode natif. C'est un très puissant motif si vous pensez à ce sujet.

Maintenant, en réalité, la commande des modèles n'est pas utilisé comme un général d'une langue telle fonction. La surcharge de transformer chaque instruction dans une classe séparée conduirait à une insupportable quantité de code réutilisable. Mais en principe, il peut être utilisé pour résoudre les mêmes problèmes que les monades sont utilisés pour résoudre dans fp.

66voto

BMeph Points 907

Dans les termes d'une OOP programmeur aurait comprendre (sans fonctionnelle arrière-plan de programmation), ce qui est un monade?

Quel problème à résoudre et ce sont les endroits les plus communs, il est utilisé?sont les endroits les plus communs, il est utilisé?

En termes de programmation orientée-objet, une monade est une interface (ou, plus probablement, un mixin), paramétrée par un type, avec deux méthodes, return et bind qui décrivent:

  • Comment injecter une valeur pour obtenir un monadique de la valeur de ce injecté valeur type;
  • Comment utiliser une fonction qui fait un monadique de la valeur à partir d'un non monadique, sur une valeur monadique

Le problème qu'il résout est le même type de problème que vous attendez de la part de toute l'interface, à savoir, "J'ai un tas de différentes classes qui font des choses différentes, mais qui semble faire des choses différentes dans la manière qu'a un sous-jacent de la similitude. Comment puis-je décrire cette similitude entre eux, même si les classes elles-mêmes ne sont pas vraiment des sous-types de quelque chose de plus proche que "l'Objet" de la classe elle-même?"

Plus précisément, l' Monad "interface" est semblable à l' IEnumerator ou IIterator en ce qu'elle prend un type qui prend elle-même un type. Le principal "point" de Monad si c'est être capable de se connecter opérations sur la base d'intérieur type, même au point d'avoir un nouveau "type interne", tout en maintenant ou même en améliorant la structure de l'information de la classe principale.

J'espère que cela a aidé.

44voto

VonC Points 414372

Vous avez une présentation récente "Monadologie -- l'aide d'un professionnel sur le type d'anxiété" par Christopher Ligue (12 juillet 2010), ce qui est tout à fait intéressant sur les sujets de la poursuite et de monade.
La vidéo va avec (slideshare) présentation est effectivement disponible sur vimeo.
La Monade partie autour de 37 minutes, sur cette vidéo d'une heure, et commence avec la slide 42 de ses 58 diapositive de la présentation.

Il est présenté comme "le principal motif de conception pour la programmation fonctionnelle", mais le langage utilisé dans les exemples est de Scala, qui est à la fois de la programmation orientée objet et fonctionnelle.
Vous pouvez en lire plus sur Monade dans la Scala dans le blog "les Monades - une Autre façon de résumé des calculs dans Scala", à partir de Debasish Ghosh (27 Mars 2008).

Un type constructeur de M est une monade si elle prend en charge ces opérations:

# the return function
def unit[A] (x: A): M[A]

# called "bind" in Haskell 
def flatMap[A,B] (m: M[A]) (f: A => M[B]): M[B]

# Other two can be written in term of the first two:

def map[A,B] (m: M[A]) (f: A => B): M[B] =
  flatMap(m){ x => unit(f(x)) }

def andThen[A,B] (ma: M[A]) (mb: M[B]): M[B] =
  flatMap(ma){ x => mb }

Ainsi, par exemple (en Scala):

  • Option est une monade
 def unité[A] (x: A): Option[A] = (x)

 def flatMap[A,B](m:Option[A])(f: =>Option[B]): Option[B] =
 m match {
 cas Aucun => Aucun
 cas Certains(x) => f(x)
}
  • List est Monade
 def unité[A] (x: A): List[A] = List(x)

 def flatMap[A,B](m:List[A])(f: =>Liste[B]): List[B] =
 m match {
 cas Nil => Nil
 affaire x::xs => f(x) ::: flatMap(xs)(f)
}

Monade sont un gros problème dans Scala en raison de la pratique de la syntaxe, conçues pour tirer parti de la Monade structures:

for de la compréhension en Scala:

for {
  i <- 1 to 4
  j <- 1 to i
  k <- 1 to j
} yield i*j*k

qui se traduit par le compilateur:

(1 to 4).flatMap { i =>
  (1 to i).flatMap { j =>
    (1 to j).map { k =>
      i*j*k }}}

La clé de l'abstraction est l' flatMap, qui lie le calcul par chaînage.
Chaque invocation d' flatMap renvoie la même structure de données de type (mais de valeur différente), qui sert d'entrée à la commande suivante dans la chaîne.

Dans l'extrait ci-dessus, flatMap prend comme entrée une fermeture (SomeType) => List[AnotherType] et renvoie un List[AnotherType]. Le point important à noter est que tous les flatMaps prendre le même type de fermeture en entrée et revenir par le même type de sortie.

C'est ce qui "lie" le calcul thread - chaque élément de la séquence dans le pour-de la compréhension est à l'honneur ce même type de contrainte.


Si vous prenez deux opérations (qui peut échouer) et de transmettre le résultat à la troisième, comme:

lookupVenue: String => Option[Venue]
getLoggedInUser: SessionID => Option[User]
reserveTable: (Venue, User) => Option[ConfNo]

mais sans prendre avantage de la Monade, vous obtenez alambiquée de la programmation orientée objet-code comme:

val user = getLoggedInUser(session)
val confirm =
  if(!user.isDefined) None
  else lookupVenue(name) match {
    case None => None
    case Some(venue) =>
      val confno = reserveTable(venue, user.get)
      if(confno.isDefined)
        mailTo(confno.get, user.get)
      confno
  }

tandis qu'avec la Monade, vous pouvez travailler avec les types réels (Venue, User) ainsi que toutes les opérations de travail, et de garder l'Option de vérification des trucs cachés, car tous les flatmaps de la syntaxe:

val confirm = for {
  venue <- lookupVenue(name)
  user <- getLoggedInUser(session)
  confno <- reserveTable(venue, user)
} yield {
  mailTo(confno, user)
  confno
}

Le rendement de la partie ne sera exécutée que si tous les trois fonctions ont Some[X]; tout None sera directement retourné à l' confirm.


Donc:

Les monades permettre ordonné de calcul au sein de la Fonctionnelle de la Programmation, qui nous permet de modéliser le séquençage des actions dans une belle forme structurée, un peu comme un LIS.

Et la plus grande puissance vient avec la possibilité de composer des monades qui servent à des fins différentes, en extensible abstractions au sein d'une application.

Ce séquençage et le filetage des actions par une monade est fait par le compilateur de langage qui ne la transformation grâce à la magie de fermetures.


Par la manière, de la Monade n'est pas seulement le modèle de calcul utilisé dans FP: voir ce post de blog.

Catégorie théorie propose de nombreux modèles de calcul. Parmi eux

  • la Flèche modèle de calculs
  • la Monade modèle de calculs
  • l'Applicatif modèle de calculs

18voto

the_drow Points 6141

De wikipedia:

En programmation fonctionnelle, une monade est une sorte de type abstrait de données utilisées pour représenter les calculs (au lieu de les données dans le modèle de domaine). Les monades permettre au programmeur de la chaîne d'actions ensemble pour la construction d'un pipeline, dans lequel chaque action est décoré avec des un traitement supplémentaire règles par la monade. Les programmes écrits en style fonctionnel peut faire usage de les monades de la structure des procédures inclure séquencé opérations,1[2] ou pour définir arbitraire de contrôle de flux (comme la gestion de la concurrence, les continuations, ou des exceptions).

Formellement, une monade est construit par la définition des deux opérations (lier et de de retour) et un constructeur de type M qui doit remplir plusieurs propriétés à permettre la composition correcte de monadique fonctions (c'est à dire des fonctions qui utilisez les valeurs de la monade comme leur les arguments). L'opération de retour prend une valeur à partir d'un simple type et la met dans un monadique conteneur de type M. L'opération de liaison effectue la le processus inverse, l'extraction de l' valeur d'origine du récipient, et de le transmettre à la prochaine fonction dans le pipeline.

Un programmeur composer monadique fonctions pour définir un traitement de données pipeline. La monade agit comme un cadre, c'est un comportement réutilisable qui décide de l'ordre dans lequel les spécifique monadique fonctions dans le les pipelines sont appelés, et gère tout le travail d'infiltration requis par la le calcul.[3] Le lier et de retour les opérateurs entrelacés dans le pipeline sera exécutée après chaque monadique la fonction renvoie le contrôle, et prendre soin de l'aspect particulier géré par la monade.

Je crois qu'il l'explique très bien.

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