web-dev-qa-db-fra.com

Quand faut-il utiliser un Kleisli?

Je suis récemment tombé sur le concept de Kleisli et chaque tutoriel/lien/référence que j'ai lu motive l'utilisation de Kleisli via les constructions suivantes:

  1. Composant des fonctions qui renvoient des monades: f: a -> m[b] avec g: b -> m[c] - Je pense que la définition très d'une monade saisit déjà ce cas - do/bind/for/flatMap faites ça. Il n'est pas nécessaire de s'appuyer sur la construction de Kleisli pour y parvenir. Cela ne peut donc pas être le cas d'utilisation "principal" d'un OMI Kleisli.
  2. Insertion de la configuration: Celui-ci indique que si plusieurs objets (types, classes de cas/données, etc.,) doivent avoir un Config injecté alors une construction de Kleisli peut être utilisée pour faire abstraction de l'injection répétable. Il existe de nombreuses façons d'y parvenir (par exemple avec implicits dans Scala) qui peuvent ne pas être nécessaires pour appeler un Kleisli. Encore une fois, l'OMI, cela ne se distingue pas comme un cas d'utilisation "primaire".
  3. Monad Transformers: Je n'ai pas une solide compréhension de cela mais voici mon interprétation: Si vous avez besoin de "composer des monades", vous avez besoin de une construction qui vous permet de paramétrer les monades elles-mêmes. Par exemple M1[M2[M1[M2[a]]]] pourrait être transformé en [M1[M2[a]]] qui pourrait (je me trompe peut-être) être aplati à travers les frontières monadiques pour être composable avec un a -> M3[b] (dire). Pour celui-ci pourrait utiliser un triplet de Kleisli et invoquer la construction car si vous le faites à partir de zéro, vous pouvez simplement réinventer le Kleisli. Ceci semble être un bon candidat pour justifier l'utilisation d'un Kleisli. Est-ce correct?

Je crois #1-#2 ci-dessus sont des "utilisations secondaires". Autrement dit, si vous utilisez la construction Kleisli, vous pouvez aussi obtenir des modèles pour composer des fonctions qui renvoient des monades ainsi que l'injection de configuration. Cependant, ils ne peuvent pas motiver des problèmes prônant le pouvoir de Kleislis.

Sous l'hypothèse d'utiliser l'abstraction la moins puissante pour résoudre le problème en question, ce qui motive les problèmes peuvent-ils être utilisés pour montrer leur utilisation?

Thèse alternative: Il est tout à fait possible que je me trompe totalement et que ma compréhension de Kleislis soit incorrecte. Je n'ai pas le fond de théorie des catégories nécessaire, mais il pourrait être qu'un Kleisli est une construction orthogonale qui peuvent être utilisés à la place des monades et ils (Kleisli) sont une lentille théorique des catégories à travers laquelle nous voyons les problèmes du monde fonctionnel (c'est-à-dire qu'un Klesli enveloppe simplement une fonction monadique a -> M[b] et maintenant nous pouvons travailler à un niveau d'abstraction plus élevé où la fonction est l'objet d'une manipulation vs un objet de utilisation ). Ainsi, l'utilisation de Kleisli peut être simplement comprise comme " Programmation fonctionnelle avec Kleisli ". Si cela est vrai, alors il devrait être une situation où un Kleisli peut résoudre un problème mieux que les constructions existantes et nous revenons à la question d'un problème de motivation . Il est également probable qu'il n'y ait pas de problème de motivation en soi, si c'est simplement un objectif qui offre différentes solutions au même problème. Lequel est-ce?

Il serait vraiment utile d'obtenir des commentaires pour reconstituer le besoin de Kleislis.

27
PhD

Parfois, nous pourrions vouloir structurer les calculs d'une manière moins expressive, plus "rigide" que l'interface complète Monad, mais aussi potentiellement plus inspectable. Kleislis peut être utilisé pour incorporer des effets monadiques dans ces calculs.

Par exemple, imaginez que nous construisons des pipelines de calcul où chaque étape est associée à une sorte d'annotation. Une annotation peut représenter une estimation du temps nécessaire pour terminer l'étape, ou d'une autre ressource liée à l'étape. Nous voulons pouvoir inspecter les annotations accumulées pour un pipeline entier avant de réellement "exécuter" ses effets:

import Prelude hiding (id,(.))
import Control.Category (Category,(.),id)
import Control.Arrow (Kleisli (..))

data Step w m i o = Step w (Kleisli m i o) 

instance (Monoid w, Monad m) => Category (Step w m) where
    id = Step mempty (Kleisli pure)
    (.) (Step wf f) (Step wg g) = Step (wg <> wf) (f . g)

Mise en œuvre:

main :: IO ()
main = do
    let Step w (Kleisli _) = 
              Step "b" (Kleisli putStrLn) 
            . Step "a" (Kleisli (\() -> getLine))
    putStrLn w
    -- result: ab
0
danidiaz