Monade dans la plaine de l'anglais? (Pour la programmation orientée objet programmeur sans FP arrière-plan)

en termes qu'un programmeur OOP pourrait comprendre (sans aucune expérience 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é?

EDIT:

pour clarifier le genre de compréhension que je cherchais, disons que vous étiez en train de convertir une application FP qui avait monads en une application OOP. Ce serait vous faites pour porter les responsabilités des monades à l'application OOP?

580
demandé sur fig 2010-04-24 17:42:54
la source

18 ответов

mise à jour: cette question a fait l'objet d'une très longue série de blogs, que vous pouvez lire à monades - merci pour la grande question!

en termes qu'un programmeur OOP comprendrait (sans aucune expérience de programmation fonctionnelle), qu'est-ce qu'un monad?

un monad est un " Amplificateur "des types que obéit à certaines règles et dont certaines opérations sont prévues .

tout d'Abord, qu'est ce qu'un "amplificateur de types"? Par cela je veux dire quelque système qui vous permet de prendre un type et le transformer en un type plus spécial. Par exemple, dans C# considérer Nullable<T> . C'est un amplificateur de types. Il vous permet de prendre un type, dire int , et ajouter une nouvelle capacité à ce type, à savoir, que maintenant il peut être null quand il ne pouvait pas avant.

comme deuxième exemple, considérez 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.

quelles sont les "certaines règles"? En bref, qu'il y a une façon raisonnable pour les fonctions sur le type sous-jacent de travailler sur le type amplifié de sorte qu'ils suivent les règles normales de la composition fonctionnelle. Par exemple, si vous avez une fonction sur entiers, dites

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

puis la fonction correspondante sur Nullable<int> peut faire fonctionner ensemble tous les opérateurs et les appels" de la même manière " qu'avant.

(c'est incroyablement vague et imprécis; vous avez demandé une explication qui ne supposait rien sur la connaissance de la composition fonctionnelle.)

quelles sont les"opérations"?

  1. il est une opération " unitaire "(parfois appelée" Opération de retour") qui prend une valeur d'un type simple et crée la valeur monadique équivalente. Ceci, en substance, fournit une façon de prendre une valeur d'un type non amplifié et de la transformer en une valeur du type amplifié. Il pourrait être implémenté comme un constructeur dans un langage OO.

  2. il y a une opération "bind" qui prend une valeur monadique et une fonction qui peut transformer la valeur, et retourne une valeur monadique. Bind est l'opération clé qui définit la sémantique de la monade. Il nous permet de transformer les opérations sur le type non amplifié en opérations sur le type amplifié, qui obéit aux règles de composition fonctionnelle mentionnées ci-dessus.

  3. il y a souvent un moyen de sortir le type non amplifié du type amplifié. Strictement parlant, cette opération n'est pas nécessaire d'avoir une monade. (Si c'est nécessaire si vous voulez avoir un comonad . Nous ne considérons pas comme ceux dans cet article.)

encore une fois, prenez Nullable<T> comme exemple. Vous pouvez transformer un int en un Nullable<int> avec le constructeur. Le compilateur C# s'occupe du "lifting" le plus nul pour vous, mais s'il ne l'a pas fait, la transformation de lifting 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 de transformer un Nullable<int> en un int est fait avec le Value propriété.

c'est la transformation de la fonction qui est le bit clé. Remarquez comment la sémantique réelle de l'opération nulle - qu'une opération sur un null propage le null - est capturée dans la transformation. Nous pouvons généraliser cela.

supposons que vous avez une fonction de int à int , comme notre original M . Vous pouvez facilement transformer cela en une fonction qui prend un int et retourne un Nullable<int> parce que vous pouvez juste exécuter le résultat à travers le constructeur nul. Supposons maintenant que vous ayez cette méthode d'ordre supérieur:

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

vous voyez ce que vous pouvez faire avec ça? Toute méthode qui prend un int et renvoie un int , ou prend un int et renvoie un Nullable<int> peut maintenant avoir la sémantique nullable appliquée à lui .

en outre: supposons que vous avez deux méthodes

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

et vous voulez les composer:

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

C'est-à-dire Z est la composition de X et Y . Mais vous ne pouvez pas faire cela parce que X prend un int , et Y retourne un Nullable<int> . Mais puisque vous avez l'opération "bind", vous peut faire ce travail:

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

l'opération de liaison sur un monad est ce qui fait fonctionner la composition des fonctions sur des types amplifiés. les "règles" que j'ai énoncées ci-dessus sont que la monade préserve les règles de la composition fonctionnelle normale; que composer avec des fonctions d'identité aboutit à la fonction originale, que la composition est associative, et ainsi de suite.

En C#, "Bind" est appelé "SelectMany". Jetez un oeil à la façon dont ça marche sur la séquence monad. Nous devons avoir deux choses: transformer une valeur en une séquence et lier les opérations sur les séquences. En bonus, nous avons aussi "tourner une séquence de retour dans une valeur". Ces opérations sont les suivantes:

static IEnumerable<T> MakeSequence<T>(T item)
{
    yield return item;
}
// Extract a value
static T First<T>(IEnumerable<T> sequence)
{
    // let's just take the first one
    foreach(T item in sequence) return item; 
    throw new Exception("No first item");
}
// "Bind" is called "SelectMany"
static 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;            
}

la règle Monad nullable était de "combiner deux fonctions qui produisent des nullables ensemble, vérifier pour voir si l'interne résulte en null; si elle le fait, produire null, si elle ne le fait pas, alors appeler l'extérieur avec le résultat". C'est la sémantique souhaitée de nullable.

la règle de la séquence monad est de "combiner deux fonctions qui produisent des séquences ensemble, d'appliquer la fonction extérieure à chaque élément produit par la fonction interne, puis de concaténer toutes les séquences résultantes ensemble". La sémantique fondamentale des monades est saisie dans les méthodes Bind / SelectMany ; c'est la méthode qui vous dit ce que la monade signifie réellement .

nous pouvons faire Pair mieux. Supposons que vous ayez une séquence d'ints, et une méthode qui prend des ints et aboutit à des séquences de cordes. Nous pourrions généraliser l'opération de liaison pour permettre la composition de fonctions qui prennent et renvoient différents types amplifiés, à condition que les entrées de l'un correspondent aux sorties de l'autre:

static 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;            
}

donc maintenant nous pouvons dire "amplifiez ce tas d'entiers individuels dans une séquence d'entiers. Transformer cet entier particulier en un tas de chaînes, amplifié par une séquence de cordes. Maintenant, mettez les deux opérations ensemble: amplifiez ce tas d'entiers dans la concaténation de toutes les séquences de cordes."Monades vous permettent de composer vos amplifications.

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

c'est un peu comme se demander "quels problèmes le modèle singleton résout-il?"mais je vais lui donner un coup.

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

  • j'ai besoin de faire de nouvelles capacités pour ce type et encore combiner d'anciennes fonctions sur ce type pour utiliser les nouvelles capacités.
  • j'ai besoin de capturer un tas d'opérations sur les types et de représenter ces opérations comme des objets composables, en construisant des compositions de plus en plus grandes jusqu'à ce que j'ai juste la bonne série d'opérations représentées, et puis je 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 effets secondaires

c# utilise monades dans sa conception. Comme nous l'avons déjà mentionné, le motif nul s'apparente fortement à la "Peut-être monade". LINQ est entièrement construit à partir de monades; la méthode SelectMany est ce qui fait le travail sémantique de la composition des opérations. (Erik Meijer est amoureux de la soulignant que chaque fonction LINQ pourrait en fait être implémentée par SelectMany ; tout le reste n'est qu'une commodité.)

pour clarifier le genre de compréhension que je cherchais, disons que vous étiez en train de convertir une application FP qui avait monads en une application OOP. Que feriez-vous pour transférer les responsabilités des monades dans l'application OOP?

la plupart des langues OOP ne avoir un système de type assez riche pour représenter le modèle monad lui-même directement; Vous avez besoin d'un système de type qui prend en charge les types qui sont des types plus élevés que les types génériques. Donc je n'essaierais pas de faire ça. Plutôt, je voudrais implémenter des types génériques qui représentent chaque monade, et implémenter des méthodes qui représentent les trois opérations dont vous avez besoin: transformer une valeur en une valeur amplifiée, (peut-être) transformer une valeur amplifiée en une valeur, et transformer une fonction sur des valeurs non amplifiées en une fonction sur des valeurs amplifiées. valeur.

un bon point de départ est la façon dont nous avons mis en œuvre LINQ dans C#. Étudiez la méthode SelectMany ; c'est la clé pour comprendre comment la séquence monad fonctionne dans C#. C'est une méthode très simple, mais très puissant!


suggestion, autre lecture:

  1. pour une explication plus approfondie et théoriquement saine des monades en C#, je recommande fortement mon ( Eric Lippert ' S) article de Wes Dyer sur le sujet. Cet article est ce qui m'a expliqué les monades quand ils ont finalement "cliqué" pour moi.
  2. une bonne illustration de pourquoi vous pourriez vouloir un monad autour de (utilise Haskell dans ses exemples) .
  3. sorte de," traduction " de L'article précédent à JavaScript.

618
répondu Eric Lippert 2018-05-16 17:13:07
la source

Pourquoi avons-nous besoin de monades?

  1. nous voulons programmer en utilisant uniquement les fonctions . ("programmation fonctionnelle" après tout -FP).
  2. alors, nous avons un premier gros problème. C'est un programme:

    f(x) = 2 * x

    g(x,y) = x / y

    Comment dire ce qui doit être exécuté en premier ? Comment Pouvons-nous former une séquence ordonnée de fonctions (i.e. un programme ) en n'utilisant que des fonctions?

    Solution: fonctions de composition . Si vous voulez d'abord g et ensuite f , écrivez f(g(x,y)) . OK, mais ...

  3. plus de problèmes: certaines fonctions peuvent échouer (i.e. g(2,0) , diviser par 0). Nous avons non "exceptions" dans FP . Comment pouvons-nous résoudre?

    Solution: let's permettent aux fonctions de retourner deux types de choses : au lieu d'avoir g : Real,Real -> Real (fonction de deux réals dans un réel), permettons g : Real,Real -> Real | Nothing (fonction de deux réals dans (réel ou rien)).

  4. Mais les fonctions (plus simple) de retour seulement une chose .

    Solution: créons un nouveau type de données à retourner, un " boxing type " qui entoure peut-être un réel ou être tout simplement rien. Par conséquent, nous pouvons avoir g : Real,Real -> Maybe Real . OK, mais ...

  5. Qu'arrive-t-il maintenant à f(g(x,y)) ? f n'est pas prêt à consommer Maybe Real . Et, nous ne voulons pas changer toutes les fonctions que nous pourrions connecter avec g pour consommer un Maybe Real .

    Solution: ont une fonction spéciale pour"connecter"/"composer"/" lien "fonctions . De cette façon, nous pouvons, en coulisses, d'adapter la sortie d'une fonction de nourrir la suivante.

    dans notre cas: g >>= f (relier/composer g à f ). Nous voulons que >>= obtienne la sortie de g , l'inspecter et, dans le cas où il est Nothing tout simplement ne pas appeler f et retourner Nothing ; ou au contraire, extraire la boîte Real et l'alimentation f avec elle. (Cet algorithme n'est que l'implémentation de >>= pour le type Maybe ).

  6. de nombreux autres problèmes peuvent être résolus en utilisant ce même schéma: 1. Utilisez une " boîte "pour codifier/stocker différentes significations/valeurs, et ont des fonctions comme g qui renvoient ces"valeurs encadrées". 2. Ont compositeurs / linkers g >>= f pour aider à relier la sortie de g à f s 'entrée, de sorte que nous n'avons pas à changer f .

  7. les problèmes remarquables qui peuvent être résolus en utilisant cette technique sont:

    • ayant un état global que chaque fonction dans la séquence de fonctions ("le programme") peut partager: solution StateMonad .

    • Nous n'aimons pas les "impurs" fonctions: fonctions de rendement différents "15191070920 de sortie" pour même "15191070920 d'entrée". Par conséquent, marquons ces fonctions, en les faisant retourner une valeur étiquetée / boxée: IO monad.

le bonheur Total !!!!

218
répondu cibercitizen1 2015-08-18 02:17:52
la source

en termes qu'un programmeur OOP comprendre (sans fonctionnelle arrière-plan de programmation), ce qui est un monade?

quel problème résout-il et quoi sont les endroits les plus communs, il est utilisé?sont les endroits les plus communs, il est utilisé?

en termes de programmation OO, un monad est une interface (ou plus probablement un mixin), paramétrée par un type, avec deux méthodes, return et bind qui décrire:

  • Comment injecter une valeur pour obtenir un valeur monade de la valeur injectée type;
  • Comment utiliser une fonction qui fait une valeur monade à 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 n'importe quelle interface, à savoir, "J'ai un tas de classes différentes qui font des choses différentes, mais semblent faire ces choses différentes d'une manière qui a un sous-jacente de la similitude. Comment puis-je décrire cette similitude entre elles, même si les classes elles-mêmes ne sont pas vraiment des sous-types de quelque chose de plus proche que la classe "L'objet" elle-même?"

plus précisément, Monad " interface "est similaire à IEnumerator ou IIterator en ce qu'il prend un type qui lui-même prend un type. Le "point" principal de Monad cependant est en mesure de relier des opérations basées sur le type intérieur, même au point d'avoir un nouveau "type interne", tout en conservant - ou même en améliorant - la structure d'information de la classe principale.

58
répondu BMeph 2015-09-06 03:31:54
la source

je dirais que l'analogie OO la plus proche des monades est le " command pattern ".

dans le schéma de commande, vous enveloppez une déclaration ou une expression ordinaire dans un objet commande . L'objet de commande expose une méthode execute qui exécute l'instruction wrapped. 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 pour que vous puissiez créer un programme-objet en enchaînant et imitant des commandes-objets.

les commandes sont exécutées par un objet séparé, le invoker . L'avantage d'utiliser le modèle de commande (plutôt que de simplement exécuter une série d'énoncés ordinaires) est que différents invokers peuvent appliquer une logique différente à la façon dont les commandes doivent être exécutées.

le modèle de commande peut être utilisé pour ajouter (ou supprimer) des fonctionnalités de langue qui n'est pas supporté par la langue d'accueil. Par exemple, dans un langage OO hypothétique sans exception, vous pouvez ajouter la sémantique d'exception en exposant les méthodes "try" et "throw" aux commandes. Lorsqu'une commande appelle throw, l'invoker fait marche arrière à travers la liste (ou l'arbre) des commandes jusqu'au dernier appel "try". Inversement, vous pouvez supprimer la sémantique d'exception d'une langue (si vous croyez les exceptions sont mauvaises ) en attrapant toutes les exceptions lancées par chaque individu les commandes, et les transformer en codes d'erreur qui sont ensuite passés à la commande suivante.

la sémantique d'exécution encore plus sophistiquée comme les transactions, l'exécution non déterministe ou les continuations peut être implémentée comme ceci dans un langage qui ne le supporte pas nativement. C'est un modèle assez puissant si vous y pensez.

maintenant, en réalité, les modèles de commandement ne sont pas utilisés comme une caractéristique de langage générale comme celle-ci. Le dessus de tourner chaque déclaration dans une catégorie distincte 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.

50
répondu JacquesB 2018-05-29 14:07:37
la source

You have a recent presentation " Monadologie -- professional help on type anxiety "by Christopher League (12 juillet 2010), ce qui est assez intéressant sur des sujets de continuation et monad.

La vidéo allant avec cette présentation (slideshare) est en fait disponible à vimeo .

La partie Monade commencez environ 37 minutes dans, sur cette vidéo d'une heure, et commence avec la diapositive 42 de sa présentation de 58 diapositives.

il est présenté comme" le modèle de conception de premier plan pour la programmation fonctionnelle", mais le langage utilisé dans les exemples est Scala, qui est à la fois OOP et fonctionnel.

Vous pouvez lire plus sur Monad en Scala dans le billet de blog " monades-une autre façon de calculs abstraits en Scala ", de Debasish Ghosh (27 Mars 2008).

Un type constructeur 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 }

par exemple (en Scala):

  • Option est un monad
    def unit[A] (x: A): Option[A] = Some(x)

    def flatMap[A,B](m:Option[A])(f:A =>Option[B]): Option[B] =
      m match {
       case None => None
       case Some(x) => f(x)
      }
  • List est Monade
    def unit[A] (x: A): List[A] = List(x)

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

Monad sont une grosse affaire à Scala en raison de la syntaxe commode construite pour tirer parti des structures Monad:

for compréhension en Scala :

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

est traduit par le compilateur:

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

l'abstraction clé est la flatMap , qui lie le calcul par chaînage.

Chaque invocation de flatMap renvoie le même type de structure de données (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 une List[AnotherType] . Le point important à noter est que tous les flatMaps prennent le même type de fermeture que input et renvoient le même type que output.

c'est Ce qui "lie" le calcul thread - chaque élément de la séquence dans le la compréhension doit respecter cette même contrainte de type.


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 profiter de Monad, vous obtenez un code-OOP alambiqué 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
  }

alors qu'avec Monad, vous pouvez travailler avec les types réels ( Venue , User ) comme toutes les opérations de travail, et de garder l'Option truc de vérification caché, tout cela à cause des flatmaps de la syntaxe for:

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

la partie rendement ne sera exécutée que si les trois fonctions ont Some[X] ; toute None sera directement retournée à confirm .


:

monades permettent le calcul ordonné dans la programmation fonctionnelle, qui nous permet de modéliser le séquençage des actions dans un nice forme structurée, un peu comme un DSL.

et la plus grande puissance vient avec la capacité de composer des monades qui servent des buts différents, dans des abstractions extensibles au sein d'une application.

ce séquençage et filetage d'actions par un monad est fait par le compilateur de langage qui fait la transformation par la magie des fermetures.


soit dit en passant, Monad n'est pas seulement modèle de calcul utilisé en FP: voir ce blog post .

la théorie des catégories propose de nombreux modèles de calcul. Parmi eux

  • la Flèche modèle de calculs
  • le modèle Monad de calculs
  • le modèle D'application des calculs
35
répondu VonC 2013-03-21 01:50:20
la source

j'ai écrit un court article comparant le code python standard OOP au code Python monadique démontrant le processus de calcul sous-jacent avec des diagrammes. Il ne suppose aucune connaissance préalable de la PF. J'espère que vous le trouverez utile - http://nikgrozev.com/2013/12/10/monads-in-15-minutes/

28
répondu Nikolay 2018-06-08 16:56:57
la source

pour respecter les lecteurs rapides, je commence par la définition précise d'abord, continuez avec une explication rapide en "anglais simple", puis passez aux exemples.

Voici une définition concise et précise légèrement reformulée:

Un monade (en informatique) est officiellement une carte:

  • envoie chaque tapez X d'un langage de programmation donné à un nouveau type T(X) (appelé le "type de T - calculs avec des valeurs en X ");

  • équipé d'une règle pour composer deux fonctions de la forme f:X->T(Y) et g:Y->T(Z) à une fonction g∘f:X->T(Z) ;

  • d'une manière qui est associative dans le sens évident et unital à l'égard d'un fonction d'unité appelée pure_X:X->T(X) , à considérer comme prenant une valeur au calcul pur qui renvoie simplement cette valeur.

ainsi, en termes simples, un monad est une règle à passer de n'importe quel type X à un autre type T(X) 15191360920", et un règle à passer de deux fonctions f:X->T(Y) et g:Y->T(Z) (que vous aimeriez composer mais can'T) à une nouvelle fonction h:X->T(Z) . Qui, cependant, n'est pas la composition au sens mathématique strict. Nous sommes essentiellement "fléchir" la composition de la fonction ou de redéfinir la façon dont les fonctions sont composées.

de plus, nous avons besoin de la règle monade de composition pour satisfaire les axiomes mathématiques "évidents":

  • associativité : composition f avec g et ensuite avec h (de l'extérieur) devrait être le même que la composition g avec h et ensuite avec f (de l'intérieur).
  • Unital property : composant f avec le identity fonction de chaque côté doit conduire à f .

encore une fois, en des mots simples, nous ne pouvons pas juste devenir fou redéfinissant notre fonction composition à notre goût:

  • nous avons d'abord besoin de l'associativité pour pouvoir composer plusieurs fonctions dans une rangée par exemple f(g(h(k(x))) , et de ne pas se soucier de spécifier l'ordre composant les paires de fonctions. Comme la règle monad prescrit seulement comment composer une paire de fonctions , sans cet axiome, nous aurions besoin de savoir quelle paire est composée en premier et ainsi de suite. (Notez que c'est différent de la propriété commutativity qui f composé avec g étaient les mêmes que g composé avec f , qui n'est pas nécessaire).
  • et deuxièmement, nous avons besoin de la propriété unital, qui est simplement de dire que les identités composent trivialement la façon dont nous les attendons. Donc nous pouvons refactoriser fonctions chaque fois que ces identités peuvent être extraites.

bref: un monad est la règle de l'extension de type et de la composition fonctions satisfaisant les deux axiomes: l'associativité et la propriété unitale.

en termes pratiques, vous voulez que le monad soit mis en œuvre pour vous par le langage, le compilateur ou le cadre qui s'occuperait de composer des fonctions pour vous. Ainsi, vous pouvez vous concentrer sur l'écriture de la logique de votre fonction plutôt que de vous inquiéter de la façon dont leur exécution est mise en œuvre.

C'est essentiellement ça, en un mot.


étant mathématicien professionnel, je préfère éviter d'appeler h la" composition "de f et g . Parce que mathématiquement, ça ne l'est pas. L'appeler la " composition "présume à tort que h est la vraie composition mathématique, ce qui n'est pas le cas. Il n'est même pas déterminé de façon unique par f et g . Au lieu de cela, c'est le résultat de la nouvelle "règle de composition" des fonctions de notre monad. Qui peut être totalement différent de la composition mathématique réelle, même si celle-ci existe!


Monade est pas un foncteur ! Un foncteur F est une règle pour passer du type X au type F(X) et des fonctions (morphisme) entre les types X et Y à des fonctions entre F(X) et F(Y) (envoi d'objets à des objets et de leurs morphismes à des morphismes dans la catégorie théorie.) Au lieu de cela un monad envoie un paire de fonctions f et g à un nouveau h .


Pour le rendre moins sec, laissez-moi essayer de l'illustrer par l'exemple que je suis en train d'annoter avec de petites sections, donc vous pouvez passer directement au point.

lancer D'Exception comme exemples de monades

supposons que nous voulions composer deux fonctions:

f: x -> 1 / x
g: y -> 2 * y

mais f(0) n'est pas défini, donc une exception e est lancé. Alors comment pouvez-vous définir la valeur de composition g(f(0)) ? Lancer à nouveau une exception, bien sûr! Peut-être le même e . Peut-être une nouvelle exception mise à jour e1 .

que se passe-t-il exactement ici? Premièrement, nous avons besoin de nouvelles valeurs d'exception(différentes ou identiques). Vous pouvez les appeler nothing ou null ou n'importe quoi mais l'essence reste même -- ils devraient être de nouvelles valeurs, par exemple, il ne devrait pas être un number dans notre exemple ici. Je préfère ne pas les appeler null pour éviter toute confusion avec la façon dont null peut être mis en œuvre dans une langue spécifique. De même , je préfère éviter nothing parce qu'il est souvent associé à null , qui, en principe, est ce que null devrait faire, cependant, ce principe est souvent plié pour quelque raison pratique que ce soit.

qu'est-ce que l'exception exactement?

c'est une affaire insignifiante pour n'importe quel programmeur expérimenté mais je voudrais laisser tomber quelques mots juste pour éteindre n'importe quel ver de confusion:

Exception est un objet encapsulant des informations sur la façon dont le résultat invalide de l'exécution s'est produit.

cela peut aller de jeter tous les détails et retourner une seule valeur globale (comme NaN ou null ) ou générer une longue liste des journaux ou ce qui s'est exactement passé, l'envoyer à une base de données et la réplication de tous les plus de la couche de stockage de données distribuées ;)

la différence importante entre ces deux exemples extrêmes d'exception est que dans le premier cas il y a pas d'effets secondaires . Dans la seconde il y en a. Ce qui nous amène à la question (mille dollars):

des exceptions sont-elles autorisées dans les fonctions pures?

réponse plus courte : Oui, mais seulement quand ils ne conduisent pas à des effets secondaires.

plus longue réponse. pour être pure, la sortie de votre fonction doit être uniquement déterminée par son entrée. Nous modifions donc notre fonction f en envoyant 0 à la nouvelle valeur abstraite e que nous appelons exception. Nous nous assurons que la valeur e ne contient aucune information extérieure qui n'est pas uniquement déterminée par notre entrée, qui est x . Voici donc un exemple d'exception sans effet secondaire:

e = {
  type: error, 
  message: 'I got error trying to divide 1 by 0'
}

et en voici un avec effet secondaire:

e = {
  type: error, 
  message: 'Our committee to decide what is 1/0 is currently away'
}

en fait, il n'a des effets secondaires que si ce message peut éventuellement changer à l'avenir. Mais si elle est garantie de ne jamais changer, cette valeur devient unique prévisible, et donc il n'y a aucun effet secondaire.

pour le rendre encore plus sillier. Une fonction de retour 42 toujours est clairement pur. Mais si quelqu'un de fou décide de faire 42 une variable que la valeur pourrait changer, la même fonction cesse d'être pure dans les nouvelles conditions.

notez que j'utilise la notation littérale de l'objet pour la simplicité afin de démontrer l'essence. Malheureusement, les choses sont en désordre dans des langages comme JavaScript, où error n'est pas un type qui se comporte comme nous le voulons ici en ce qui concerne la composition des fonctions, alors que les types réels comme null ou NaN ne pas se comporter de cette manière, mais plutôt aller à travers les quelques artificielle et pas toujours intuitive conversions de type.

extension de Type

comme nous voulons faire varier le message à l'intérieur de notre exception, nous déclarons vraiment un nouveau type E pour l'ensemble de l'objet d'exception et puis C'est ce que fait le maybe number , en dehors de son nom confus, qui doit être soit de type number ou de la nouvelle exception tapez E , c'est donc bien l'union number | E de number et E . En particulier, cela dépend de la façon dont nous voulons construire E , qui n'est ni suggérée ni reflétée dans le nom maybe number .

Qu'est-ce que la composition fonctionnelle?

il est l'opération mathématique prenant des fonctions f: X -> Y et g: Y -> Z et la construction leur composition en fonction h: X -> Z satisfaisant h(x) = g(f(x)) . Le problème avec cette définition se produit lorsque le résultat f(x) n'est pas admis comme argument de g .

en mathématiques ces fonctions ne peuvent pas être composées sans travail supplémentaire. La solution strictement mathématique pour notre exemple ci-dessus de f et g est de supprimer 0 de l'ensemble de la définition de f . Avec ce nouvel ensemble de définition (nouveau type plus restrictif de x ), f devient composable avec g .

cependant, il n'est pas très pratique dans la programmation de restreindre l'ensemble de la définition de f comme cela. Au lieu de cela, des exceptions peuvent être utilisées.

ou comme une autre approche, des valeurs artificielles sont créées comme NaN , undefined , null , Infinity etc. Vous évaluez donc 1/0 à Infinity et 1/-0 à -Infinity . Et puis forcer le retour de la nouvelle valeur dans votre expression au lieu de jeter l'exception. Conduisant à des résultats que vous pouvez ou ne pouvez pas trouver prévisible:

1/0                // => Infinity
parseInt(Infinity) // => NaN
NaN < 0            // => false
false + 1          // => 1

Et nous sommes de retour à des numéros réguliers prêt à passer ;)

JavaScript nous permet de continuer à exécuter des expressions numériques à tout prix sans jeter des erreurs comme dans l'exemple ci-dessus. Cela signifie qu'il permet aussi de composer des fonctions. Ce qui est exactement ce monade est d'environ - c'est une règle de composer des fonctions satisfaisant les axiomes tels que définis au début de cette réponse.

mais la règle de la fonction de composition, découlant de L'implémentation de JavaScript pour traiter les erreurs numériques, est-elle une monade?

pour répondre à cette question, tout ce que vous avez besoin est de vérifier les axiomes (à gauche comme l'exercice ne fait pas partie de la question ici;).

Peut lancer une exception être utilisé pour construire une monade?

en effet, une monade plus utile serait plutôt la règle prescrivant que si f jette l'exception pour quelque x , ainsi fait sa composition avec n'importe quel g . Plus faire l'exception E globalement unique avec une seule valeur possible jamais ( terminal object dans la théorie des catégories). Maintenant les deux axiomes sont vérifiables instantanément et nous obtenons une monade très utile. Et le résultat est ce qui est bien connu comme le peut-être monad .

23
répondu Dmitri Zaitsev 2016-11-28 17:28:53
la source

un monad est un type de données qui encapsule une valeur et auquel, essentiellement, deux opérations peuvent être appliquées:

  • return x crée une valeur du type monad qui encapsule x
  • m >>= f (à lire comme" l'opérateur bind") applique la fonction f à la valeur dans le monad m
  • "

C'est ce qu'est une monade. Il y a un peu plus aspects techniques , mais essentiellement ces deux opérations définissent un monad. La vraie question Est, " Qu'est-ce qu'une monade fait ?", et cela dépend des monades-les listes sont des monades, Les peut-être sont des monades, les opérations D'IO sont des monades. Tout ce que cela signifie Quand nous disons que ces choses sont des monades est qu'elles ont l'interface monade de return et >>= .

19
répondu Chuck 2018-03-23 11:02:22
la source

à Partir de wikipedia :

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

formellement, une monade est construite par définition de deux opérations (bind et 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 argument.) Retour l'opération prend une valeur d'un type simple et le met dans un conteneur monadique de type M. L'opération de liaison effectue la le processus inverse, l'extraction de l' valeur originale du contenant et le passer à la suivante associée fonction dans le pipeline.

un programmeur composera monadic 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 calcul.[3] Le lier et de retour opérateurs interleaved dans le pipeline sera exécuté après chaque monade 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.

10
répondu the_drow 2010-04-24 17:45:53
la source

je vais essayer de faire la définition la plus courte que je puisse gérer en utilisant des termes OOP:

une classe générique CMonadic<T> est un monade si elle définit au moins les méthodes suivantes:

class CMonadic<T> { 
    static CMonadic<T> create(T t);  // a.k.a., "return" in Haskell
    public CMonadic<U> flatMap<U>(Func<T, CMonadic<U>> f); // a.k.a. "bind" in Haskell
}

et si les lois suivantes s'appliquent à tous les types T et à leurs valeurs possibles t

à gauche de l'identité:

CMonadic<T>.create(t).flatMap(f) == f(t)

droit à l'identité

instance.flatMap(CMonadic<T>.create) == instance

associativité:

instance.flatMap(f).flatMap(g) == instance.flatMap(t => f(t).flatMap(g))

exemples :

Une Liste monade peut-être:

List<int>.create(1) --> [1]

et flatMap sur la liste [1,2,3] pourrait fonctionner comme cela:

intList.flatMap(x => List<int>.makeFromTwoItems(x, x*10)) --> [1,10,2,20,3,30]

itérables et Observables peuvent également être faits monadique, ainsi que des promesses et des tâches.

commentaire :

les monades ne sont pas si compliquées. Le flatMap fonction est un peu comme le plus souvent rencontré map . Il reçoit un argument de fonction (aussi connu sous le nom de delegate), qu'il peut appeler (immédiatement ou plus tard, zéro ou plusieurs fois) avec une valeur provenant de la classe generic. Il s'attend à ce que la fonction passée envelopper également sa valeur de retour dans le même type de classe générique. Pour cela, il fournit des create , un constructeur qui peut créer une instance de cette classe générique à partir d'une valeur. Le résultat de retour de flatMap est aussi un classe générique du même type, contenant souvent les mêmes valeurs qui étaient contenues dans les résultats de retour d'une ou plusieurs applications de flatMap aux valeurs précédemment contenues. Cela vous permet d'enchaîner flatMap autant que vous voulez:

intList.flatMap(x => List<int>.makeFromTwo(x, x*10))
       .flatMap(x => x % 3 == 0 
                   ? List<string>.create("x = " + x.toString()) 
                   : List<string>.empty())

Il se trouve que ce genre de classe générique est utile comme un modèle de base pour un grand nombre de choses. Ceci (avec les jargonismes de la théorie des catégories) est la raison pour laquelle les monades semblent si difficiles à comprendre ou à expliquer. Ils sont très abstraits et ne deviennent évidemment utiles que lorsqu'ils sont spécialisés.

par exemple, vous pouvez modéliser des exceptions en utilisant des conteneurs monadiques. Chaque conteneur contiendra soit le résultat de l'opération, soit l'erreur qui s'est produite. La fonction suivante (delegate) dans la chaîne de callbacks flatMap ne sera appelée que si la précédente contient une valeur dans le conteneur. Dans le cas contraire, si une erreur a été emballée, l'erreur continuera de se propager à travers le conteneurs enchaînés jusqu'à ce qu'un conteneur soit trouvé qui a une fonction de gestionnaire d'erreurs attachée par l'intermédiaire d'une méthode appelée .orElse() (une telle méthode serait une extension autorisée)

Notes : les langues fonctionnelles vous permettent d'écrire des fonctions qui peuvent fonctionner sur n'importe quelle sorte de classe générique monadique. Pour que cela fonctionne, il faudrait écrire une interface générique pour monades. Je ne sais pas si c'est possible d'écrire une telle interface en C#, mais pour autant que je sachez que ce n'est pas:

interface IMonad<T> { 
    static IMonad<T> create(T t); // not allowed
    public IMonad<U> flatMap<U>(Func<T, IMonad<U>> f); // not specific enough,
    // because the function must return the same kind of monad, not just any monad
}
8
répondu Gorgi Kosev 2016-01-11 17:09:42
la source

si une monade a une interprétation "naturelle" dans OO dépend de la monade. Dans un langage comme Java, vous pouvez traduire le monad peut-être vers le langage de vérification des pointeurs null, de sorte que les calculs qui échouent (i.e., ne produisent rien dans Haskell) émettent des pointeurs null comme résultats. Vous pouvez traduire l'état monade dans la langue générée en créant une variable mutable et des méthodes pour changer son état.

un monad est un monoïde de la catégorie des endofunctors.

les informations que cette phrase rassemble sont très profondes. Et vous travaillez dans une monade avec n'importe quel langage impératif. Une monade est une "séquencé" langage spécifique au domaine. Il satisfait certaines propriétés intéressantes, qui dans l'ensemble font une monade un modèle mathématique de "programmation impérative". Haskell permet de définir facilement des langues impératives petites (ou grandes), qui peuvent être combinées de différentes façons.

comme OO programmeur, vous utilisez la hiérarchie de classe de votre langue pour organiser les types de fonctions ou de procédures qui peuvent être appelées dans un contexte, ce que vous appelez un objet. Une monade est aussi une abstraction sur cette idée, dans la mesure où différentes monades peuvent être combinées de manière arbitraire, "important" effectivement toutes les méthodes de la sous-monade dans le champ d'application.

sur le plan architectural, on utilise ensuite des signatures de type pour exprimer explicitement les contextes qui peuvent être utilisés pour calculer une valeur.

on peut utiliser des transformateurs monad à cet effet, et il existe une collection de haute qualité de tous les monades "standard":

  • Listes (non-déterministe des calculs, par le traitement d'une liste de domaine)
  • Peut-être (calculs qui peuvent échouer, mais pour lesquelles la déclaration est sans importance)
  • (Erreur de calculs qui peuvent échouer et nécessitent la manipulation d'exception
  • Reader (calculs qui peuvent être représentés par des compositions de plaine Haskell fonctions)
  • Écrivain (calculs avec séquentielle "rendu"/"connexion" (à cordes, html, etc)
  • Cont (suites)
  • IO (calculs qui dépendent du système informatique sous-jacent)
  • État (calculs dont le contexte contient une valeur modifiable)

avec la monade correspondante transformateurs et classes de type. Les classes de Type permettent une approche complémentaire de la combinaison des monades en unifiant leurs interfaces, de sorte que les monades en béton peuvent implémenter une interface standard pour le monad "kind". Par exemple, le module de Contrôle.Monade.L'état contient une classe MonadState S m, et (États s) est une instance du formulaire

instance MonadState s (State s) where
    put = ...
    get = ...

la longue histoire est qu'un monad est un foncteur qui attache "contexte" à une valeur, qui a une façon d'injecter une valeur dans le monad, et qui a une façon d'évaluer les valeurs par rapport au contexte qui s'y rattache, du moins de manière restreinte.

:

return :: a -> m a

est une fonction qui injecte une valeur de type a dans une" action " monade de type M A.

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

est une fonction qui prend une monade action, évalue son résultat, et applique une fonction à la suite. La chose intéressante à propos de (>>=) c'est que le résultat est dans la même monade. Dans d'autres termes, m >>= f, (>>=) tire la conséquence de m, et le lie à f, de sorte que le résultat est dans la monade. (Alternativement, on peut dire que (>>=) tire f dans m et l'applique sur le résultat.) Par conséquent, si nous avons f:: a - > m b, et g:: b -> m c, nous pouvons "séquencer" les actions:

m >>= f >>= g

Ou, à l'aide de "faire de la notation"

do x <- m
   y <- f x
   g y

le type pour (>>) peut être lumineux. C'est

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

It correspond à l'opérateur (;) dans les langues procédurales comme C. Il permet de faire la notation comme:

m = do x <- someQuery
       someAction x
       theNextAction
       andSoOn

dans la logique mathématique et philosophique, nous avons des cadres et des modèles, qui sont "naturellement" modelés avec le monadisme. Une interprétation est une fonction qui examine le domaine du modèle et calcule la valeur de vérité (ou généralisations) d'une proposition (ou formule, sous généralisations). Dans une logique modale par nécessité, on pourrait dire qu'une proposition est nécessaire si c'est vrai "dans chaque monde possible" - si cela est vrai à l'égard de chaque domaine admissibles. Cela signifie qu'un modèle dans un langage pour une proposition peut être réifié comme un modèle dont le domaine consiste en une collection de modèles distincts (un correspondant à chaque monde possible). Chaque monade a une méthode appelée "join" qui aplatit les couches, ce qui implique que chaque action monade dont le résultat est une action monade peut être intégrée dans la monade.

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

Plus important encore, cela signifie que la monade est fermée dans le cadre de l'opération d ' "empilage de couches". C'est ainsi que les transformateurs monad fonctionnent: ils combinent des monades en fournissant des méthodes" articulées "pour des types comme

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

afin que nous puissions transformer une action en (MaybeT m) en une action en m, en faisant s'effondrer les couches. Dans ce cas, runMaybeT :: MaybeT m a -> m (peut-être a) est notre méthode join-like. (MaybeT m) est une monade, et MaybeT :: m (Peut-être) -> MaybeT m est un effectivement un constructeur pour un nouveau type d'action monad en M.

un monad libre pour un functeur est le monad généré par empiler f, avec l'implication que chaque séquence de constructeurs pour f est un élément du monad libre (ou, plus exactement, quelque chose avec la même forme que l'arbre de séquences de constructeurs pour f). Les monades libres sont une technique utile pour construire des monades flexibles avec une quantité minimale de plaque chauffante. Dans un programme Haskell, je pourrais utiliser monades gratuites pour définir des monades simples pour la "programmation système de haut niveau" pour aider à maintenir la sécurité du type (j'utilise juste les types et leurs déclarations. Les implémentations sont simples avec l'utilisation de combinateurs):

data RandomF r a = GetRandom (r -> a) deriving Functor
type Random r a = Free (RandomF r) a


type RandomT m a = Random (m a) (m a) -- model randomness in a monad by computing random monad elements.
getRandom     :: Random r r
runRandomIO   :: Random r a -> IO a (use some kind of IO-based backend to run)
runRandomIO'  :: Random r a -> IO a (use some other kind of IO-based backend)
runRandomList :: Random r a -> [a]  (some kind of list-based backend (for pseudo-randoms))

le Monadisme est l'architecture sous-jacente de ce que l'on pourrait appeler le modèle" interprète "ou" commande", abstrait à sa forme la plus claire, puisque chaque calcul monadique doit être" exécuté", au moins trivialement. (Le système d'exécution exécute L'IO monad pour nous, et est le point d'entrée à n'importe quel programme de Haskell. IO "lecteurs" le reste des calculs, en exécutant IO actions dans l'ordre).

le type pour joindre est aussi où nous obtenons la déclaration qu'un monad est un monoïde dans la catégorie des endofuncteurs. L'adhésion est généralement plus importante à des fins théoriques, en vertu de son type. Mais comprendre le type signifie comprendre les monades. Les types de jointures de transformateur et de transformateur monad sont effectivement des compositions de endofoncteurs, au sens de composition fonctionnelle. Pour le mettre dans un pseudo-langage Haskell-like,

Foo :: m (m) <-> (m . m) un

5
répondu nomen 2012-12-03 09:03:05
la source

un monad est un ensemble de fonctions

(Pst: un tableau de fonctions n'est qu'un calcul).

en fait, au lieu d'un vrai tableau (une fonction dans un tableau de cellules) vous avez ces fonctions enchaînées par une autre fonction >>=. Le >>= permet d'adapter les résultats de la fonction i pour alimenter la fonction i+1, Effectuer des calculs entre eux ou, même, de ne pas appeler la fonction i+1.

les types utilisés ici sont des "types avec contexte". C'est, d'une valeur avec un "tag". Les fonctions étant enchaînées doivent prendre une "valeur nue" et retourner un résultat étiqueté. Une des tâches de > > = est d'extraire une valeur nue de son contexte. Il y a aussi la fonction "return", qui prend une valeur nue et la met avec une étiquette.

un exemple avec peut-être . Utilisons-le pour stocker un entier simple sur lequel faire des calculs.

-- a * b
multiply :: Int -> Int -> Maybe Int
multiply a b = return  (a*b)

-- divideBy 5 100 = 100 / 5
divideBy :: Int -> Int -> Maybe Int
divideBy 0 _ = Nothing -- dividing by 0 gives NOTHING
divideBy denom num = return (quot num denom) -- quotient of num / denom

-- tagged value
val1 = Just 160 

-- array of functions feeded with val1
array1 = val1 >>= divideBy 2  >>= multiply 3 >>= divideBy  4 >>= multiply 3

-- array of funcionts created with the do notation
-- equals array1 but for the feeded val1
array2 :: Int -> Maybe Int
array2 n = do
       v <- divideBy 2  n
       v <- multiply 3 v
       v <- divideBy 4 v
       v <- multiply 3 v
       return v

-- array of functions, 
-- the first >>= performs 160 / 0, returning Nothing
-- the second >>= has to perform Nothing >>= multiply 3 ....
-- and simply returns Nothing without calling multiply 3 ....
array3 = val1 >>= divideBy 0  >>= multiply 3 >>= divideBy  4 >>= multiply 3

main = do
     print array1
     print (array2 160)
     print array3

juste pour montrer que les monades sont un ensemble de fonctions avec des opérations de helper, considérons l'équivalent de l'exemple ci-dessus, en utilisant simplement un tableau réel de fonctions

type MyMonad = [Int -> Maybe Int] -- my monad as a real array of functions

myArray1 = [divideBy 2, multiply 3, divideBy 4, multiply 3]

-- function for the machinery of executing each function i with the result provided by function i-1
runMyMonad :: Maybe Int -> MyMonad -> Maybe Int
runMyMonad val [] = val
runMyMonad Nothing _ = Nothing
runMyMonad (Just val) (f:fs) = runMyMonad (f val) fs

et il serait utilisé comme ceci:

print (runMyMonad (Just 160) myArray1)
4
répondu cibercitizen1 2014-02-16 20:28:25
la source

monades dans l'usage typique sont l'équivalent fonctionnel des mécanismes de traitement des exceptions de la programmation procédurale.

dans les langages procéduraux modernes, vous mettez un gestionnaire d'exception autour d'une séquence de déclarations, dont l'une peut jeter une exception. Si l'un des états déclenche une exception, l'exécution normale de la séquence d'instructions s'arrête et passe à un gestionnaire d'exception.

langages de programmation Fonctionnelle, cependant, philosophiquement éviter les caractéristiques de manipulation d'exception en raison de la nature "goto" comme eux. Selon la perspective de la programmation fonctionnelle, les fonctions ne devraient pas avoir d'effets secondaires comme les exceptions qui perturbent le flux des programmes.

en réalité, les effets secondaires ne peuvent pas être exclus dans le monde réel en raison principalement de I / O. monades dans la programmation fonctionnelle sont utilisés pour gérer cela en prenant un ensemble d'appels de fonction enchaînés (dont l'un pourrait produire un résultat inattendu) et tourner résultat inattendu dans des données encapsulées qui peuvent encore circuler en toute sécurité à travers les appels de fonction restants.

le flux de contrôle est préservé, mais l'événement inattendu est encapsulé et manipulé en toute sécurité.

2
répondu David K. Hess 2012-12-24 20:00:10
la source

si vous avez déjà utilisé Powershell, les modèles décrits par Eric devraient vous sembler familiers. Powershell cmdlets sont des monades; la composition fonctionnelle est représentée par a pipeline .

Jeffrey snover fait de l'entretien avec Erik Meijer va plus dans le détail.

1
répondu Richard Berg 2010-04-25 19:18:42
la source

Voir mon réponse "Ce qui est une monade?"

il commence par un exemple motivant, travaille à travers l'exemple, dérive un exemple d'une monade, et définit formellement"monad".

il ne suppose aucune connaissance de la programmation fonctionnelle et il utilise le pseudocode avec la syntaxe function(argument) := expression avec les expressions les plus simples possibles.

ce programme C++ est une implémentation du pseudocode monad. (Pour référence: M est le constructeur du type, feed est l'opération" bind", et wrap est l'opération" return".)

#include <iostream>
#include <string>

template <class A> class M
{
public:
    A val;
    std::string messages;
};

template <class A, class B>
M<B> feed(M<B> (*f)(A), M<A> x)
{
    M<B> m = f(x.val);
    m.messages = x.messages + m.messages;
    return m;
}

template <class A>
M<A> wrap(A x)
{
    M<A> m;
    m.val = x;
    m.messages = "";
    return m;
}

class T {};
class U {};
class V {};

M<U> g(V x)
{
    M<U> m;
    m.messages = "called g.\n";
    return m;
}

M<T> f(U x)
{
    M<T> m;
    m.messages = "called f.\n";
    return m;
}

int main()
{
    V x;
    M<T> m = feed(f, feed(g, wrap(x)));
    std::cout << m.messages;
}
1
répondu Jordan 2017-05-23 15:02:57
la source

en termes D'OO, un monad est un conteneur fluent.

l'exigence minimale est une définition de class <A> Something qui supporte un constructeur Something(A a) et au moins une méthode Something<B> flatMap(Function<A, Something<B>>)

peut-être, cela compte aussi si votre classe monad a des méthodes avec la signature Something<B> work() qui préserve les règles de la classe -- le compilateur fait des erreurs dans flatMap au moment de la compilation.

pourquoi un monad est-il utile? Parce que c'est un conteneur qui permet des opérations en chaîne qui préservent la sémantique. Par exemple, Optional<?> préserve la sémantique d'isPresent pour Optional<String> , Optional<Integer> , Optional<MyClass> , etc.

comme exemple approximatif,

Something<Integer> i = new Something("a")
  .flatMap(doOneThing)
  .flatMap(doAnother)
  .flatMap(toInt)

Note Nous commençons par une chaîne et finissons par un entier. Assez cool.

Dans OO, il pourrait prendre un peu la main en agitant, mais n'importe quelle méthode sur quelque Chose qui renvoie à une autre sous-classe de quelque Chose de la rencontre critère d'une fonction de conteneur qui renvoie un conteneur du type original.

c'est ainsi que l'on préserve la sémantique, c'est-à-dire que le sens et les opérations du conteneur ne changent pas, ils enveloppent et mettent en valeur l'objet à l'intérieur du conteneur.

1
répondu Rob 2016-11-30 00:39:28
la source

d'un point de vue pratique (résumant ce qui a été dit dans de nombreuses réponses précédentes et articles connexes), Il me semble que l'un des "buts" fondamentaux (ou l'utilité) du monad est de tirer parti des dépendances implicites dans les invocations méthode récursive alias composition de la fonction (i.e. lorsque f1 appelle F2 appelle f3, f3 doit être évalué avant f2 avant f1) pour représenter la composition séquentielle d'une manière naturelle, en particulier dans le contexte d'un modèle d'évaluation paresseux (qui est, la composition séquentielle comme une séquence simple, par exemple" f3(); f2(); f1 (); " en C - l'astuce est particulièrement évidente si vous pensez à un cas où f3, f2 et f1 ne retournent réellement rien [leur enchaînement comme f1(F2(f3)) est artificiel, purement destiné à créer une séquence]).

ceci est particulièrement important quand il y a des effets secondaires, c'est-à-dire quand un État est modifié (si f1, F2, f3 n'ont pas d'effets secondaires, il ne serait pas important dans quel ordre ils sont évalués; ce qui est une grande propriété de purs langages fonctionnels, pour pouvoir paralléliser ces calculs par exemple). Plus les fonctions sont pures, mieux c'est.

je pense que de ce point de vue étroit, les monades pourraient être considérées comme du sucre syntaxique pour les langues qui favorisent l'évaluation paresseuse (qui évaluent les choses seulement quand c'est absolument nécessaire, suivant un ordre qui ne repose pas sur la présentation du code), et qui n'ont aucun autre moyen de représenter la composition séquentielle. Le résultat net est que les sections de code qui sont "impurs" (c'est-à-dire qui ont des effets secondaires) peuvent être présentés naturellement, d'une manière impérative, mais sont clairement séparés des fonctions pures (sans effets secondaires), qui peuvent être évalués paresseusement.

ce n'est qu'un aspect cependant, comme averti ici .

0
répondu novis 2013-04-07 05:15:17
la source

en termes qu'un programmeur OOP comprendrait (sans aucune expérience de programmation fonctionnelle), qu'est-ce qu'un monad?

il n'y a pas une telle explication que je sache, et la suggestion qu'il existe contredit l'arrogance de la part des programmeurs OOP.

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

les monades résolvent beaucoup différents problèmes avec une interface unifiée, qui est pourquoi ils sont utiles. La principale chose pour laquelle ils sont utilisés est la paresse - ils permettent des fonctions paresseuses avec des effets secondaires, ce qui est important pour FFI et autres. Ils sont également couramment utilisés pour le traitement des erreurs et parsing.

pour clarifier le genre de compréhension que je cherchais, disons que vous étiez en train de convertir une application FP qui avait monads en une application OOP. Que feriez-vous pour le port responsabilités des monades à l'égard de l'app OOP?

vu que la paresse n'est pas courante dans L'OOP, donc dans la plupart des cas, ils ne seraient pas pertinents. Vous pouvez cependant porter la manipulation d'erreur monadic.

0
répondu 2018-03-31 20:51:20
la source

Autres questions sur functional-programming oop monads