web-dev-qa-db-fra.com

Que signifie «courir» à l'intérieur d'une monade?

En travaillant à travers les chapitres des manuels Haskell sur différentes monades, je me perds à plusieurs reprises lorsque les auteurs passent de l'explication des détails de bind et des lois des monades à l'utilisation réelle des monades. Soudain, des expressions telles que "exécuter une fonction dans un contexte monadique" ou "exécuter une monade" apparaissent. De même, dans la documentation de la bibliothèque et dans les discussions sur les piles de transformateurs monades, j'ai lu des déclarations selon lesquelles certaines fonctions "peuvent être exécutées dans n'importe quel choix de monade". Que signifie exactement ce "courir à l'intérieur d'une monade"?

Il y a deux choses que je ne semble pas comprendre:

  1. Une monade est une classe de type avec des fonctions (return, >>=) et les lois. "Exécuter" quelque chose à l'intérieur d'une monade pourrait donc signifier soit (a) le fournir comme argument à return, soit (b) le séquencer en utilisant >>=. Si la monade est de type m a, alors dans le cas a) que quelque chose doit être de type a, pour correspondre au type de la fonction return. Dans le cas b) que quelque chose doit être une fonction de type a -> m b, pour correspondre au type de >>= fonction. De là, je ne comprends pas comment je peux "exécuter" une fonction à l'intérieur d'une monade arbitraire, car les fonctions que je séquence en utilisant >>= doivent tous avoir la même signature de type, et les valeurs que j'élève à l'aide de return doivent être du paramètre de type monade spécifique.
  2. Dans ma compréhension, il n'y a pas de notion de exécution ou en cours d'exécution un calcul dans un langage fonctionnel - il n'y a qu'une application de fonction à un argument, et l'évaluation de la fonction (en la remplaçant par Sa valeur). Pourtant, de nombreuses monades spécifiques sont livrées avec une fonction run telle que runReader, runState, etc. Ces fonctions ne font pas partie de la définition d'une monade, et ce sont des fonctions simples , en aucun cas des déclarations impératives spéciales en dehors du noyau fonctionnel du langage. Alors, qu'est-ce qu'ils "courent"?

Je pense qu'avoir une compréhension claire de ces concepts est la clé pour comprendre les piles de transformateurs monades ou les constructions similaires qui semblent être nécessaires pour comprendre toutes les bibliothèques importantes et tous les programmes non triviaux dans Haskell. Merci beaucoup de m'avoir aidé à passer de la simple écriture de code fonctionnel à la compréhension de ce que cela signifie.

6
Ulrich Schuster

les fonctions que je séquence en utilisant >> = doivent toutes avoir la même signature de type

Ce n'est qu'un peu vrai. Dans certains contextes monadiques, nous pouvons avoir l'expression

x >>= f >>= g

x :: Maybe Int
f :: Int -> Maybe String
g :: String -> Maybe Char

Tous ces éléments doivent impliquer la même monade (Peut-être), mais notez qu'ils n'ont pas tous la même signature de type. Tout comme avec la composition de fonction ordinaire, vous n'avez pas besoin que tous les types de retour soient identiques, simplement que l'entrée d'une fonction corresponde à la sortie de son prédécesseur.

1
amalloy

Voici une analogie simple de "exécuter la fonction à l'intérieur du conteneur", avec un pseudo-code:

Disons que vous avez un type Future [String] qui représente un conteneur qui aura une chaîne "à un moment donné dans le futur":

val Tweet: Future[String] = getTweet()

Maintenant, vous voulez accéder à la chaîne - mais vous ne sortez pas la chaîne du contexte - le "futur" - vous utilisez simplement la chaîne "à l'intérieur du conteneur":

Tweet.map { str =>
  println(str)
}

À l'intérieur de ces accolades, vous êtes "dans le futur". Par exemple:

val Tweet: Future[String] = getTweet()

Tweet.map { str =>
  println(str)
}

println("Length of Tweet string is " + Tweet.length) // <== WRONG -- you are not yet in the future

Le Tweet.length tente d'accéder au Tweet en dehors du conteneur. Donc "être à l'intérieur du conteneur" est analogue à, lors de la lecture du code source, "être à l'intérieur des accolades d'une carte (flatmap, etc.)". Vous plongez à l'intérieur du conteneur.

Tweet.map { str =>
  println("Length of Tweet string is " + str.length) // <== RIGHT
}

Bien que ce soit une analogie très simple, je trouve cela utile lorsque je pense à toutes les monades en général. Dans le code source, où se trouve-t-on "à l'intérieur du conteneur" et où en est-on à l'extérieur? Dans ce cas, la fonction de longueur est exécutée dans le futur, ou "à l'intérieur du conteneur".

0
Jim Flood