Que fait l’opérateur Haskell ?

Passer en revue la documentation de Haskell est toujours un peu pénible pour moi, car toutes les informations que vous obtenez sur une fonction ne sont souvent rien de plus que: fa -> f [a] ce qui pourrait signifier n’importe quel nombre de choses.

Comme c’est le cas de la fonction .

Tout ce que je donne c’est ceci: () :: fa -> fa -> fa et que c’est une “opération binary associative”

Après inspection de Control.Applicative j’apprends qu’il fait des choses apparemment sans rapport selon la mise en œuvre.

 instance Alternative Maybe where empty = Nothing Nothing  r = r l  _ = l 

Ok, donc ça revient bien s’il n’y a plus de gauche, sinon ça retourne à gauche, gotcha .. Cela m’amène à penser que c’est un opérateur “de gauche ou de droite”, ce qui est logique vu son utilisation de | et | utilisation historique comme “OU”

 instance Alternative [] where empty = [] () = (++) 

Sauf qu’ici, il suffit d’appeler l’opérateur de concaténation de la liste … Briser mon idée …

Alors, quelle est exactement cette fonction? Quel est son utilisation? Où cela s’inscrit-il dans le grand schéma des choses?

    Typiquement, cela signifie “choix” ou “parallèle” en ce sens a <|> b est soit un “choix” de a ou de b ou de a et b fait en parallèle. Mais revenons en arrière.

    En réalité, il n’y a pas de sens pratique aux opérations dans des classes de types comme (<*>) ou (<|>) . Ces opérations ont un sens de deux manières: (1) via les lois et (2) via les instanciations. Si nous ne parlons pas d’une instance particulière d’ Alternative alors seul (1) est disponible pour avoir un sens intuitif.

    Donc “associatif” signifie qu’un a <|> (b <|> c) est identique à (a <|> b) <|> c . Ceci est utile car cela signifie que nous ne nous préoccupons que de la séquence des choses enchaînées avec (<|>) , et non de leur “structure arborescente”.

    D’autres lois incluent l’identité avec le empty . En particulier, a <|> empty = empty <|> a = a . Dans notre intuition avec “choix” ou “parallèle”, ces lois se lisent comme “un ou (quelque chose d’impossible) doit être un” ou “un côté (processus vide) est juste un”. Il indique que empty est une sorte de “mode de défaillance” pour une Alternative .

    Il existe d’autres lois avec comment (<|>) / empty interagissent avec fmap (à partir de Functor ) ou pure / (<*>) (à partir de Applicative ), mais peut-être le meilleur moyen de comprendre la signification de (<|>) est d’examiner un exemple très courant d’un type qui instancie Alternative : un Parser .

    Si x :: Parser A et y :: Parser B alors (,) <$> x <*> y :: Parser (A, B) parsing x puis y en séquence. En revanche, (fmap Left x) <|> (fmap Right y) parsing x ou y , en commençant par x , pour essayer les deux parsings possibles. En d’autres termes, il indique une twig dans votre arbre d’parsing, un choix ou un univers d’parsing parallèle.

    (<|>) :: fa -> fa -> fa en dit beaucoup, même sans tenir compte des lois pour Alternative .

    Il prend deux valeurs fa et doit en rendre un. Il faudra donc combiner ou sélectionner ses entrées d’une manière ou d’une autre. Il est polymorphe dans le type a , il sera donc totalement incapable d’inspecter les valeurs de type a pourraient se trouver dans un fa ; cela signifie qu’il ne peut pas faire la “combinaison” en combinant des valeurs, il doit donc le faire uniquement en fonction de la structure que le constructeur de type f ajoute.

    Le nom aide un peu aussi. Une sorte de “OU” est en effet le concept vague que les auteurs essayaient d’indiquer avec le nom “Alternative” et le symbole “<|>“.

    Maintenant, si j’ai deux valeurs et Maybe a je dois les combiner, que puis-je faire? Si elles sont toutes les deux Nothing Je devrai retourner Nothing , sans aucun moyen de créer un a . Si au moins l’un d’entre eux est un Just ... je peux renvoyer une de mes entrées telle quelle ou je peux retourner Nothing . Il y a très peu de fonctions qui sont même possibles avec le type Maybe a -> Maybe a -> Maybe a , et pour une classe dont le nom est “Alternative”, celle donnée est assez raisonnable et évidente.

    Que diriez-vous de combiner deux [a] valeurs? Il y a plus de fonctions possibles ici, mais en réalité, c’est assez évident. Et le nom “Alternative” vous donne une bonne idée de ce que cela pourrait être si vous êtes familier avec l’interprétation standard de “non-déterminisme” de la liste monad / applicative; si vous voyez un [a] comme un “non a déterministe a ” avec une collection de valeurs possibles, la manière évidente de “combiner deux valeurs non a déterministes” d’une manière qui mériterait le nom “Alternative” est de produire une non a détermination pourrait être l’une des valeurs de l’une des entrées.

    Et pour les parsingurs la combinaison de deux parsingurs syntaxiques a deux interprétations évidentes qui viennent à l’esprit; soit vous produisez un parsingur qui correspond à ce que le premier fait et ensuite ce que fait le second, soit vous produisez un parsingur qui correspond à ce que fait le premier ou à ce que le second fait (il y a place pour les options). Étant donné le nom “Alternative”, l’interprétation “ou” semble très naturelle pour <|> .

    Donc, vues d’un niveau d’abstraction suffisamment élevé, ces opérations font toutes la même chose. La classe de type sert en réalité à opérer à ce niveau d’abstraction élevé où toutes ces choses “se ressemblent”. Lorsque je travaille sur une seule instance connue, je pense à l’opération <|> exactement comme elle le fait pour ce type spécifique.

    Un exemple intéressant d’ Alternative qui n’est pas un parsingur syntaxique ou une chose de type MonadPlus est Concurrently , un type très utile du paquet async .

    Pour Concurrently , empty est un calcul qui dure éternellement. Et (<|>) exécute ses arguments simultanément, renvoie le résultat du premier qui se termine et annule l’autre.

    Celles-ci semblent très différentes, mais considérez:

     Nothing <|> Nothing == Nothing [] <|> [] == [] Just a <|> Nothing == Just a [a] <|> [] == [a] Nothing <|> Just b == Just b [] <|> [b] == [b] 

    Donc, ce sont en fait très, très similaires , même si la mise en œuvre semble différente. La seule vraie différence est ici:

     Just a <|> Just b == Just a [a] <|> [b] == [a, b] 

    Un Maybe ne peut contenir qu’une seule valeur (ou zéro, mais aucun autre montant). Mais bon, s’ils étaient tous les deux identiques , pourquoi auriez-vous besoin de deux types différents? Le fait qu’ils soient différents est, vous le savez, différent .

    En résumé, la mise en œuvre peut sembler totalement différente, mais celles-ci sont en réalité assez similaires.