70 votes

Confus par le sens de la classe de types 'Alternative' et ses relations avec les autres classes de types

J'ai été en passant par la Typeclassopedia à savoir le type de classes. Je suis coincé à la compréhension Alternative (et MonadPlus, pour cette question).

Les problèmes que je vais avoir:

  • le " pedia dit que "l'Alternative de type classe est pour les foncteurs Applicatifs qui ont également une structure de monoïde." Je ne comprends pas ce -- n'a pas d'Alternative dire quelque chose de totalement différent de Monoïde? c'est à dire que j'ai compris le point de l'Alternative type de classe que de choisir entre deux choses, alors que j'ai compris Monoids comme étant la combinaison de choses.

  • pourquoi ne Alternatifs besoin d'un empty de la méthode/de membre? J'ai peut-être tort, mais il semble ne pas être utilisé à tous ... au moins dans le code que j'ai pu trouver. Et il ne semble pas cadrer avec le thème de la classe -- si j'ai deux choses, et la nécessité de choisir un, de quoi ai-je besoin d'un 'vide' pour?

  • pourquoi ne l'Alternative de type classe besoin d'un Applicatif de contrainte, et pourquoi est-il besoin d'une sorte d' * -> *? Pourquoi ne pas simplement avoir <|> :: a -> a -> a? Toutes les instances pourraient encore être mises en œuvre dans le même sens ... je crois (pas sûr). Quelle valeur est-il prévoir que le Monoïde ne l'est pas?

  • quel est le point de l' MonadPlus type de classe? Je ne peux pas déverrouiller tous les de sa bonté en utilisant simplement quelque chose d'à la fois comme un Monad et Alternative? Pourquoi ne pas simplement fossé il? (Je suis sûr que je me trompe, mais je n'ai pas de contre-exemples)

Espérons que toutes ces questions sont cohérentes ... !


Bounty mise à jour: @Antal la réponse est un bon début, mais Q3 est toujours ouvert: ce n'Alternative prévoir que Monoïde ne l'est pas? Je trouve cette réponse insatisfaisante puisqu'il manque d'exemples concrets, et une discussion de la façon dont le supérieur kindedness de Alternatif qui la distingue de Monoïde.

Si c'est de combiner applicative effets avec Monoïde de comportement, pourquoi ne pas simplement:

liftA2 mappend

C'est encore plus confus pour moi parce que beaucoup de Monoïde instances sont exactement les mêmes que les autres instances.

C'est pourquoi je suis à la recherche d' exemples précis qui montrent pourquoi les autres est nécessaire, et comment il est différent, ou signifie quelque chose de différent-à partir de Monoïde.

8voto

AndrewC Points 21273

Résumé

  • Nous avons besoin de définir (instances qui fournissent les mêmes opérations que) Monoïde cas pour certains foncteurs applicatifs, sincèrement, combiner le foncteur applicatif de niveau, et pas seulement de levage niveau inférieur monoids. L'exemple d'erreur ci-dessous à partir de litvar = liftA2 mappend literal variable montre que <|> ne peut en général être défini comme l' liftA2 mappend; <|> fonctionne dans ce cas, par la combinaison d'analyseurs, pas leurs données.

  • Si l'on utilise le Monoïde directement, nous aurions besoin d'extensions de langage pour définir les instances. Alternative est supérieur kinded de sorte que vous pouvez faire ces cas, sans exiger des extensions de langage.

Exemple: Analyseurs

Imaginons que nous faisons l'analyse des déclarations, de sorte que nous importons tout ce que nous allons avoir besoin de

import Text.Parsec
import Text.Parsec.String
import Control.Applicative ((<$>),(<*>),liftA2,empty)
import Data.Monoid
import Data.Char

et de réfléchir à la façon dont nous allons analyser un type. Nous choisissons simpliste:

data Type = Literal String | Variable String  deriving Show
examples = [Literal "Int",Variable "a"]

Maintenant, nous allons écrire un analyseur de deux types:

literal :: Parser Type
literal = fmap Literal $ (:) <$> upper <*> many alphaNum

Sens: analyser un upperde cas de caractères, alors many alphaNumeric personnages, combiner les résultats dans une seule Chaîne avec la fonction pure, (:). Ensuite, appliquer la fonction pure, Literal les Strings dans Types. Nous allons analyser les types de variables exactement de la même façon, sauf pour le démarrage avec un lowerde cas lettre:

variable :: Parser Type
variable = fmap Variable $ (:) <$> lower <*> many alphaNum

C'est excellent, et parseTest literal "Bool" == Literal "Bool" exactement comme nous l'espérions.

La Question 3a: Si c'est de combiner applicative effets avec Monoïde de comportement, pourquoi ne pas simplement liftA2 mappend

Edit:Oups - oublié de l'utiliser réellement <|>!
Maintenant, nous allons combiner ces deux analyseurs à l'aide d'autres:

types :: Parser Type
types = literal <|> variable

Cela peut analyser n'importe quel Type: parseTest types "Int" == Literal "Bool" et parseTest types "a" == Variable "a". Cette combine les deux analyseurs, pas les deux valeurs. C'est le sens dans lequel il travaille à l'Applicatif Foncteur niveau, plutôt que le niveau de données.

Cependant, si nous essayons de nous:

litvar = liftA2 mappend literal variable

ce serait de demander au compilateur de combiner les deux valeurs qu'ils génèrent, au niveau des données. Nous obtenons

No instance for (Monoid Type)
  arising from a use of `mappend'
Possible fix: add an instance declaration for (Monoid Type)
In the first argument of `liftA2', namely `mappend'
In the expression: liftA2 mappend literal variable
In an equation for `litvar':
    litvar = liftA2 mappend literal variable

Nous avons donc découvert la première chose; l'autre classe n'quelque chose de véritablement différent de liftA2 mappend,, car il combine des objets à un niveau différent - il combine les analyseurs, pas de l'analyse de données. Si vous aimez à penser à elle de cette façon, c'est une combinaison à la véritable plus-type de niveau, et non pas simplement un ascenseur. Je n'aime pas dire cela de cette façon, parce qu' Parser Type a *, mais il est vrai de dire que nous sommes en combinant l' Parsers, pas le Types.

(Même pour les types avec un Monoïde exemple, liftA2 mappend ne vous donnera pas le même analyseur <|>. Si vous l'essayez sur Parser String vous obtiendrez liftA2 mappend qui analyse l'un après l'autre puis concatène, contre <|> qui va tenter le premier et l'analyseur de défaut à la seconde si elle a échoué.)

Question 3b: En quoi l'Alternative de l' <|> :: f a -> f a -> f a diffèrent de Monoïde de l' mappend :: b -> b -> b?

Tout d'abord, vous avez raison de noter qu'il n'a pas à fournir de nouvelles fonctionnalités sur un Monoïde instance.

Deuxièmement, cependant, il y a un problème avec l'utilisation de Monoïde directement: Nous allons essayer d'utiliser mappend sur les analyseurs, en même temps que de montrer que c'est la même structure que l' Alternative:

instance Monoid (Parser a) where
    mempty = empty
    mappend = (<|>)

Oups! Nous obtenons

Illegal instance declaration for `Monoid (Parser a)'
  (All instance types must be of the form (T t1 ... tn)
   where T is not a synonym.
   Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Monoid (Parser a)'

Donc, si vous avez un foncteur applicatif f, l' Alternative exemple montre que l' f a est un monoïde, mais vous ne peut que déclarer que, en tant que Monoid avec une extension du langage.

Une fois que nous ajoutons {-# LANGUAGE TypeSynonymInstances #-} dans le haut du fichier, nous allons bien, et peut définir

typeParser = literal `mappend` variable

et pour notre plus grand plaisir, il travaille: parseTest typeParser "Yes" == Literal "Yes" et parseTest typeParser "a" == Literal "a".

Même si vous n'avez pas de synonymes (Parser et String sont des synonymes, de sorte qu'ils sont), vous aurez toujours besoin d' {-# LANGUAGE FlexibleInstances #-} à définir une instance comme celle-ci:

data MyMaybe a = MyJust a | MyNothing deriving Show
instance Monoid (MyMaybe Int) where
   mempty = MyNothing
   mappend MyNothing x = x
   mappend x MyNothing = x
   mappend (MyJust a) (MyJust b) = MyJust (a + b)

(Le monoïde exemple, Peut-être est-ce en soulevant le sous-jacent monoïde.)

Faire une bibliothèque standard inutilement dépend des extensions de langage n'est évidemment pas souhaitable.


Donc là vous l'avez. Alternative est juste Monoïde pour les Foncteurs Applicatifs (et ce n'est pas seulement un ascenseur, d'un Monoïde). Il a besoin de la plus-kinded type f a -> f a -> f a de sorte que vous pouvez définir l'un sans les extensions de langage.

Vos autres Questions, par souci d'exhaustivité:

  1. Pourquoi ne Alternatifs besoin d'une méthode vide/membre?
    Parce qu'avoir une identité pour une opération est parfois utile. Par exemple, vous pouvez définir anyA = foldr (<|>) empty sans l'utilisation fastidieuse des cas limites.

  2. quel est le point de la MonadPlus type de classe? Je ne peux pas déverrouiller tous les de sa bonté, juste en utilisant quelque chose comme une Monade et Alternative? Pas de. Je vous renvoie à la question que vous avez lié à:

En outre, même si Applicative a été une super-classe de la Monade, vous feriez liquidation nécessitant la MonadPlus classe de toute façon, parce que l'obéissance empty <*> m = empty n'est pas strictement suffisant pour prouver qu' empty >>= f = empty.

....et je suis venu avec un exemple: Peut-être. Je l'explique en détail, avec la preuve dans cette réponse à Antal la question. Pour les fins de cette réponse, il est intéressant de noter que j'ai été en mesure d'utiliser >>= pour faire de la MonadPlus instance, qui a battu l'Alternative des lois.


Structure de monoïde est utile. L'Alternative est la meilleure façon de fournir pour les Foncteurs Applicatifs.

4voto

Matt Fenwick Points 17097

Je ne vais pas couvrir MonadPlus car il y a désaccord au sujet de ses lois.


Après avoir essayé et à défaut de trouver un quelconque des exemples significatifs dans lequel la structure d'un Applicatif conduit naturellement à une autre instance qui est en désaccord avec son Monoïde instance*, je suis enfin arrivé à ceci:

L'Alternative lois sont plus strictes que les Monoïde, parce que le résultat ne dépend l'intérieur type. Ce qui exclut un grand nombre de Monoïde des instances de solutions de rechange. Ces types de données permettent partielle (ce qui signifie qu'ils ne fonctionnent que pour certains intérieure types) Monoïde des instances qui sont interdites par les extra "structure" de la * -> * nature. Exemples:

  • le standard Peut-être exemple pour Monoïde suppose que l'intérieur est de type Monoïde => ce n'est pas une Alternative

  • ZipLists, les tuples et les fonctions peuvent toutes être Monoids, si leur intérieure types sont Monoids => pas de solutions de rechange

  • des séquences qui ont au moins un élément, ne peuvent pas être des solutions de rechange, car il n'y a pas d' empty:

    data Seq a
        = End a
        | Cons a (Seq a)
      deriving (Show, Eq, Ord)
    

D'autre part, certains types de données ne peuvent pas être faites Alternatives, car ils sont l' *-kinded:

  • unité -- ()
  • Ordering
  • nombres, booléens

Mon déduit conclusion: pour les types qui ont à la fois une Alternative et un Monoïde exemple, les instances sont destinés à être le même. Voir aussi cette réponse.


l'exclusion Peut-être, qui je le souligne, ne compte pas parce que ses instance ne devrait pas nécessiter de Monoïde pour l'intérieur type, auquel cas elle serait identique à la Variante

2voto

MathematicalOrchid Points 15354

J'ai compris le point de l'Alternative type de classe que de choisir entre deux choses, alors que j'ai compris Monoids comme étant la combinaison de choses.

Si vous pensez à ce sujet pendant un moment, ils sont les mêmes.

L' + combine des choses (généralement des nombres), et c'est le type de signature est - Int -> Int -> Int (ou autre).

L' <|> de l'opérateur de choisir entre les alternatives, et c'est le type de signature est également le même: prendre deux correspondants choses et le retour d'un combiné chose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X