web-dev-qa-db-fra.com

Que fait l'opérateur <|> de Haskell?

Parcourir 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: f a -> f [a] ce qui pourrait signifier un certain nombre de choses.

Comme c'est le cas de la fonction <|>.

Tout ce qu'on me donne c'est: (<|>) :: f a -> f a -> f a et que c'est une "opération binaire associative" ...

Lors de l'inspection de Control.Applicative j'apprends qu'il fait des choses apparemment indépendantes selon la mise en œuvre.

instance Alternative Maybe where
    empty = Nothing
    Nothing <|> r = r
    l       <|> _ = l

Ok, donc il retourne à droite s'il n'y a pas de gauche, sinon il retourne à gauche, gotcha .. Cela m'amène à croire que c'est un opérateur "gauche ou droite", ce qui est un peu logique étant donné son utilisation de | et | utilisation historique de "OR"

instance Alternative [] where
    empty = []
    (<|>) = (++)

Sauf qu'ici, il appelle simplement l'opérateur de concaténation de la liste ... Décomposer mon idée ...

Alors, quelle est exactement cette fonction? Quel est son utilisation? Où se situe-t-il dans le grand schéma des choses?

64
Electric Coffee

Typiquement, cela signifie "choix" ou "parallèle" en ce que a <|> b Est soit un "choix" de a ou b ou a et b fait en parallèle. Mais revenons en arrière.

En réalité, il n'y a aucune signification pratique aux opérations dans des classes comme (<*>) Ou (<|>). Ces opérations ont un sens de deux manières: (1) via des lois et (2) via des instanciations. Si nous ne parlons pas d'une instance particulière de Alternative, alors seulement (1) est disponible pour la signification intuitive.

Donc "associatif" signifie que a <|> (b <|> c) est identique à (a <|> b) <|> c. Ceci est utile car cela signifie que nous ne nous soucions que de la séquence de choses enchaînées avec (<|>), Pas de leur "arborescence".

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

Il existe d'autres lois sur la façon dont (<|>)/empty interagissent avec fmap (de Functor) ou pure/(<*>) (à partir de Applicative), mais peut-être que la meilleure façon de progresser dans la compréhension de la signification de (<|>) consiste à examiner un exemple très courant d'un type qui instancie Alternative: a Parser.

Si x :: Parser A Et y :: Parser B Alors (,) <$> x <*> y :: Parser (A, B) Analyse x puis y en séquence. En revanche, (fmap Left x) <|> (fmap Right y) Analyse soit x ou y, en commençant par x, pour essayer les deux analyses possibles. En d'autres termes, il indique une branche dans votre arbre d'analyse, un choix ou un univers d'analyse parallèle.

66
J. Abrahamson

(<|>) :: f a -> f a -> f a vous en dit beaucoup, même sans tenir compte des lois de Alternative.

Il faut être deux f a valeurs, et doit en redonner une. Il devra 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 complètement incapable d'inspecter les valeurs de type a qui pourraient être à l'intérieur d'un f a; cela signifie qu'il ne peut pas faire la "combinaison" en combinant les valeurs de a, donc il doit le faire uniquement en fonction de la structure ajoutée par le constructeur de type f.

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

Maintenant, si j'ai deux Maybe a valeurs et je dois les combiner, que puis-je faire? S'ils sont tous les deux Nothing je vais avoir pour retourner Nothing, sans moyen de créer un a. Si au moins l'un d'eux est un Just ... Je peux retourner une de mes entrées telle quelle ou je peux retourner Nothing. Il y a très peu de fonctions qui sont même possible 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 c'est vraiment assez évident ce que cela est susceptible de faire. Et le nom "Alternative" vous donne une bonne idée de ce que cela risque d'être fourni vous êtes familier avec l'interprétation "non déterministe" standard de la liste monade/applicative; si vous voyez un [a] en tant que "non déterministe a" avec une collection de valeurs possibles, alors la manière évidente de "combiner deux valeurs non déterministes a" d'une manière qui pourrait mériter le nom "Alternative" est de produire un a non déterministe qui pourrait être l'une des valeurs de l'une ou l'autre des entrées.

Et pour les analyseurs; la combinaison de deux analyseurs a deux interprétations larges évidentes qui me viennent à l'esprit; soit vous produisez un analyseur qui correspondrait à ce que fait le premier puis à ce que fait le second, soit vous produisez un analyseur qui correspond à soit ce que fait le premier o ce que fait le second (il y a bien sûr des détails subtils sur chacune de ces options qui laissent de la place aux options). Étant donné le nom "Alternative", l'interprétation "ou" semble très naturelle pour <|>.

Ainsi, vues à partir d'un niveau d'abstraction suffisamment élevé, ces opérations faire "font toutes la même chose". La classe de type est vraiment destinée à fonctionner à ce niveau d'abstraction élevé où toutes ces choses "se ressemblent". Lorsque j'opère sur une seule instance connue, je pense simplement au <|> opération comme exactement ce qu'elle fait pour ce type spécifique.

35
Ben

Ces semblent très différents, 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 l'implémentation 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 que n valeur (ou zéro, mais pas tout 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 savez, être différent.

En résumé, le implémentation peut sembler totalement différent, mais ceux-ci sont en fait assez similaires.

12
MathematicalOrchid

Un exemple intéressant d'un Alternative qui n'est pas un analyseur ou une chose semblable à MonadPlus est Concurrently , un type très utile du async paquet.

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

12
danidiaz