Verward door de betekenis van de ‘Alternatieve’ typeklasse en de relatie met andere typeklassen

Ik heb de Typeclassopediadoorlopen om de typeklassen te leren. Ik begrijp Alternative(en trouwens MonadPlus) niet meer.

De problemen die ik ondervind:

  • de ‘pedia’ zegt dat “de klasse Alternative is voor Applicatieve functors die ook een monoïde structuur hebben.” Ik snap dit niet — betekent Alternative niet iets totaal anders dan Monoid? d.w.z. ik begreep het punt van de klasse Alternative als kiezen tussen twee dingen, terwijl ik begreep dat Monoids ging over het combineren van dingen.

  • waarom heeft Alternative een emptymethode/lid nodig? Ik kan het mis hebben, maar het lijkt helemaal niet te worden gebruikt … tenminste in de codedie ik kon vinden. En het lijkt niet te passen bij het thema van de klas — als ik twee dingen heb en er één moet kiezen, waar heb ik dan een ‘leeg’ voor nodig?

  • waarom heeft de klasse Alternative een toepassingsbeperking nodig, en waarom heeft deze een soort * -> *? Waarom niet gewoon <|> :: a -> a -> a? Alle instanties kunnen nog steeds op dezelfde manier worden geïmplementeerd … denk ik (niet zeker). Welke waarde biedt het dat Monoid niet biedt?

  • wat is het nut van de klasse van het type MonadPlus? Kan ik niet al zijn goedheid ontsluiten door iets te gebruiken als zowel een Monadals een Alternative? Waarom niet gewoon dumpen? (Ik weet zeker dat ik het mis heb, maar ik heb geen tegenvoorbeelden)

Hopelijk zijn al die vragen coherent … !


Bounty-update: het antwoord van @Antal is een goed begin, maar Q3 is nog open: wat biedt Alternative dat Monoid niet biedt? Ik vind dit antwoordonbevredigend omdat het geen concrete voorbeelden en een specifiek discussie over hoe de hogere vriendelijkheid van Alternative het onderscheidt van Monoid.

Als het is om de effecten van applicative te combineren met het gedrag van Monoid, waarom niet gewoon:

liftA2 mappend

Dit is nog verwarrender voor mij omdat veel Monoid-instanties precies hetzelfde zijn als de Alternative-instanties.

Daarom ben ik op zoek naar specifieke voorbeeldendie laten zien waarom Alternative nodig is, en hoe het anders is — of iets anders betekent — van Monoid.


Antwoord 1, autoriteit 100%

Laat me om te beginnen korte antwoorden geven op elk van deze vragen. Ik zal elk antwoord vervolgens uitbreiden tot een langer gedetailleerd antwoord, maar deze korte antwoorden zullen hopelijk helpen bij het navigeren door die antwoorden.

  1. Nee, Alternativeen Monoidbetekenen niet verschillende dingen; Alternativeis voor typen die de structuur hebben van zowel Applicativeals Monoid. “Kiezen” en “combineren” zijn twee verschillende intuïties voor hetzelfde bredere concept.

  2. Alternativebevat zowel emptyals <|>omdat de ontwerpers dachten dat dit nuttig zou zijn en omdat dit geeft aanleiding tot een monoïde. In termen van picken komt emptyovereen met het maken van een onmogelijke keuze.

  3. We hebben zowel Alternativeals Monoidnodig omdat de eerste meerwetten gehoorzaamt (of zou moeten) dan de laatste; deze wetten hebben betrekking op de monoïdale en applicatieve structuur van de typeconstructor. Bovendien kan Alternativeniet afhankelijk zijn van het innerlijke type, terwijl Monoiddat wel kan.

  4. MonadPlusis iets sterker dan Alternative, omdat het aan meer wetten moet voldoen; deze wetten relateren de monoïdale structuur aan de monadische structuur naast de applicatieve structuur. Als je exemplaren van beide hebt, moeten ze samenvallen.


Betekent Alternativeniet iets totaal anders dan Monoid?

Niet echt! Een deel van de reden voor uw verwarring is dat de Haskell Monoid-klasse een aantal behoorlijk slechte (nou ja, onvoldoende algemene) namen gebruikt. Dit is hoe een wiskundige een monoïde zou definiëren (er heel expliciet over zijn):

Definitie.Een monoïdeis een set Muitgerust met een onderscheidend element εM en een binaire operator · : M× MM, aangegeven door nevenschikking, zodat de volgende twee voorwaarden gelden:

  1. εis de identiteit: voor alle mM, mε= εm= m.
  2. · is associatief: voor alle m₁,m₂,m₃ ∈ M, (mm₂)m₃ = m₁(mm₃).

Dat is het. In Haskell wordt ε gespeld als memptyen · wordt gespeld als mappend(of tegenwoordig <>), en de set Mis het type Min instance Monoid M where ....

Als we naar deze definitie kijken, zien we dat er niets staat over ‘combineren’ (of over ‘picken’). Het zegt dingen over · en over ε, maar dat is alles. Het is zeker waar dat het combineren van dingen goed werkt met deze structuur: εkomt overeen met geen dingen hebben, en mm₂ zegt dat als Ik glom de spullen van m₁ en m₂ bij elkaar, ik kan een nieuw ding krijgen met al hun spullen. Maar hier is een alternatieve intuïtie: εkomt overeen met helemaal geen keuzes, en mm₂ komt overeen met een keuze tussen m₁ en m₂. Dit is de “pluk”-intuïtie. Merk op dat beide de monoïde wetten gehoorzamen:

  1. Helemaal niets hebben en geen keuze hebben zijn beide de identiteit.
    • Als ik geen spullen heb en het samen met wat spullen doe, krijg ik weer hetzelfde spul.
    • Als ik een keuze heb tussen helemaal geen keuze (iets onmogelijks) en een andere keuze, moet ik de andere (mogelijke) keuze kiezen.
  2. Bloeiende collecties bij elkaar en een keuze maken zijn beide associatief.
    • Als ik drie verzamelingen dingen heb, maakt het niet uit of ik de eerste twee samen en dan de derde, of de laatste twee samen en dan de eerste; hoe dan ook, ik eindig met dezelfde totale glommed-collectie.
    • Als ik de keuze heb tussen drie dingen, maakt het niet uit of ik (a) eerst kies tussen eerste of tweede en derde en dan, indien nodig, tussen eerste en tweede, of (b) eerst kies tussen eerste en tweede of derde en dan, als het moet, tussen tweede en derde. Hoe dan ook, ik kan kiezen wat ik wil.

(Opmerking: ik speel hier snel en losjes; daarom is het intuïtie. Het is bijvoorbeeld belangrijk om te onthouden dat · niet commutatief hoeft te zijn, wat het bovenstaande verdoezelt: het is perfect mogelijk dat mm₂ ≠ mm₁.)

Zie: beide soorten dingen (en vele andere – is het vermenigvuldigen van getallen echt ofwel “combineren” of“picken”?) voldoen aan dezelfde regels. Een intuïtie hebben is belangrijk om begrip te ontwikkelen, maar het zijn de regels en definities die bepalen wat er eigenlijkaan de hand is.

En het beste is dat deze beide intuïties door dezelfde drager kunnen worden geïnterpreteerd! Laat Meen verzameling verzamelingen zijn (niet een verzameling van alleverzamelingen!) die de lege verzameling bevat, laat εde lege verzameling zijn ∅, en laat · worden ingesteld unie ∪. Het is gemakkelijk in te zien dat ∅ een identiteit is voor ∪, en dat ∪ associatief is, dus we kunnen concluderen dat (M,∅,∪) een monoïde is. Nu:

  1. Als we sets beschouwen als verzamelingen van dingen, dan komt ∪ overeen met ze samenvoegen om meer dingen te krijgen – de “combinerende” intuïtie.
  2. Als we sets beschouwen als mogelijke acties, dan komt ∪ overeen met het vergroten van je pool van mogelijke acties waaruit je kunt kiezen: de ‘picking’-intuïtie.

En dit is precies wat er aan de hand is met []in Haskell: [a]is een Monoidvoor alle a, en []als een applicatieve functor (en monade) wordt gebruikt om niet-determinisme weer te geven. Zowel de combinatie- als de pick-intuïties vallen samen in hetzelfde type: mempty = empty = []en mappend = (<|>) = (++).

Dus de klasse Alternativeis er alleen om objecten weer te geven die (a) applicatieve functors zijn, en (b) wanneer ze worden geïnstantieerd op een type, een waarde en een binaire functie hebben die volgen op een aantal reglement. Welke regels? De monoïde regels. Waarom? Omdat het handig blijkt te zijn 🙂


Waarom heeft Alternativeeen lege methode/lid nodig?

Nou, het snarky antwoord is “omdat Alternativeeen monoïde structuur vertegenwoordigt.” Maar de echte vraag is: waaromeen monoïde structuur? Waarom niet gewoon een semigroep, een monoïde zonder ε? Eén antwoord is om te beweren dat monoïden gewoon nuttiger zijn. Ik denk dat veel mensen (maar misschien niet Edward Kmett) het hiermee eens zijn; bijna altijd, als u een verstandige (<|>)/mappend/· heeft, kunt u een verstandige empty/empty/ε. Aan de andere kant is het prettig om de extra algemeenheid te hebben, omdat je dan meer dingen onder de paraplu kunt plaatsen.

U wilt ook weten hoe dit aansluit bij de “picking”-intuïtie. In gedachten houdend dat, in zekere zin, het juiste antwoord is “weet wanneer je de ‘pluk’-intuïtie moet opgeven”, denk ik dat je de twee kuntverenigen. Overweeg [], de applicatieve functor voor niet-determinisme. Als ik twee waarden van het type [a]combineer met (<|>), komt dat overeen met het niet-deterministisch kiezen van een actie van links of een actie van rechts . Maar soms heb je aan één kant geen mogelijke acties – en dat is prima. Evenzo, als we parsers beschouwen, vertegenwoordigt (<|>)een parser die ofwel wat zich aan de linkerkant of wat aan de rechterkant is, parseert (hij “kiest”). En als je een parser hebt die altijd faalt, wordt dat een identiteit: als je die kiest, verwerp je die keuze onmiddellijk en probeer je de andere.

Dit alles gezegd hebbende, onthoud dat het zouheel goed mogelijk zijn om een ​​klasse te hebben die bijna lijkt op Alternative, maar die emptymist. Dat zou volkomen terecht zijn – het zou zelfs een superklasse van Alternativekunnen zijn – maar dat is toevallig niet wat Haskell deed. Vermoedelijk is dit geen idee van wat nuttig is.


Waarom heeft de klasse van het Alternative-type een Applicative-beperking nodig, en waarom heeft het een soort * -> *? … Waarom niet gewoon [gebruik] liftA2 mappend?

Nou, laten we eens kijken naar elk van deze drie voorgestelde wijzigingen: het wegwerken van de Applicative-beperking voor Alternative; het soort argument van Alternativewijzigen; en gebruik liftA2 mappendin plaats van <|>en pure memptyin plaats van empty. We zullen eerst naar deze derde wijziging kijken, omdat deze het meest anders is. Stel dat we Alternativevolledig hebben verwijderd en de klasse vervangen door twee gewone functies:

fempty :: (Applicative f, Monoid a) => f a
fempty = pure mempty
(>|<) :: (Applicative f, Monoid a) => f a -> f a -> f a
(>|<) = liftA2 mappend

We zouden zelfs de definities van someen manykunnen behouden. En dit geeftons een monoïde structuur, dat is waar. Maar het lijkt alsof het ons de verkeerde geeft. Moet Just fst >|< Just sndmislukken, aangezien (a,a) -> ais geen instantie van Monoid? Nee, maar dat is wat de bovenstaande code zou opleveren. De monoïde instantie die we willenis er een die innerlijk agnostisch is (om terminologie te lenen van Matthew Farkas-Dyckin een zeer verwante haskell-café-discussiedie een aantal zeer vergelijkbare vragen stelt); de Alternativestructuur gaat over een monoïde bepaald door de structuur van f, niet de structuur van het argument van f.

Nu we denken dat we Alternativewillen laten staan ​​als een soort typeklasse, laten we eens kijken naar de twee voorgestelde manieren om dit te veranderen. Als we de soort veranderen, moetenwe de Applicativebeperking opheffen; Applicativespreekt alleen over soort dingen * -> *, en er is dus geen manier om ernaar te verwijzen. Dat laat twee mogelijke veranderingen over; de eerste, meer kleine, verandering is het wegwerken van de Applicative-beperking, maar laat het soort met rust:

class Alternative' f where
  empty' :: f a
  (<||>) :: f a -> f a -> f a

De andere, grotere verandering is om de Applicative-beperking op te heffen en het soort te veranderen:

class Alternative'' a where
  empty'' :: a
  (<|||>) :: a -> a -> a

In beide gevallen moeten we some/manyverwijderen, maar dat is oké; we kunnen ze definiëren als zelfstandige functies met het type (Applicative f, Alternative' f) => f a -> f [a]of (Applicative f, Alternative'' (f [a])) => f a -> f [a].

Nu, in het tweede geval, waar we het soort type variabele veranderen, zien we dat onze klasse precies hetzelfde is als Monoid(of, als je nog steeds empty'', Semigroup), dus het heeft geen voordeel om een ​​aparte klas te hebben. En zelfs als we de variabele soort alleen laten, maar de beperking Applicativeverwijderen, wordt Alternativegewoon forall a. Monoid (f a), hoewel we deze gekwantificeerde beperkingen niet in Haskell kunnen schrijven, zelfs niet met alle mooie GHC-extensies. (Merk op dat dit het agnosticisme van het innerlijke type uitdrukt dat hierboven is genoemd.) Als we dus een van deze wijzigingen kunnen aanbrengen, hebben we geen reden om Alternativete behouden (behalve dat we die gekwantificeerde beperking, maar dat lijkt nauwelijks dwingend).

Dus de vraag komt neer op “is er een relatie tussen de Alternativedelen en de Applicativedelen van een fdie een instantie is van beide?” En hoewel er niets in de documentatie staat, ga ik een standpunt innemen en jazeggen – of op zijn minst zoumoeten zijn. Ik denk dat Alternativeverondersteld wordt te gehoorzamen aan enkele wetten met betrekking tot Applicative(naast de monoïde wetten); in het bijzonder denk ik dat die wetten zoiets zijn als

  1. Juiste verdeling (van <*>):  (f <|> g) <*> a = (f <*> a) <|> (g <*> a)
  2. Rechter opname (voor <*>):  empty <*> a = empty
  3. Linker distributiviteit (van fmap):  f <$> (a <|> b) = (f <$> a) <|> (f <$> b)
  4. Linker absorptie (voor fmap):  f <$> empty = empty

Deze wetten lijken waar te zijn voor []en Maybe, en (doen alsof de MonadPlus-instantie een Alternativeinstance) IO, maar ik heb geen bewijzen of uitgebreide tests gedaan. (Ik dacht bijvoorbeeld oorspronkelijk dat linker-distributiviteit werd aangehouden voor <*>, maar dit “voert de effecten uit” in de verkeerde volgorde voor [].) Bij wijze van analogie is het echter waar dat van MonadPluswordt verwacht dat het zich aan soortgelijke wetten houdt (hoewel er bestaat blijkbaar onduidelijkheid over). Ik had oorspronkelijk een derde wet willen claimen, wat natuurlijk lijkt:

  • Linker absorptie (voor <*>):  a <*> empty = empty

Hoewel ik geloof dat []en Maybezich aan deze wet houden, doet IOdat niet, en ik denk (om redenen die wordt duidelijk in de volgende paar alinea’s) is het het beste om het niet te vereisen.

En inderdaad, het lijkt erop dat Edward Kmett een aantal dia’s heeftwaar hij een soortgelijke mening aanhangt; om daar op in te gaan, moeten we een korte uitweiding maken met wat meer wiskundig jargon. De laatste dia, “I Want More Structure”, zegt dat “Een monoide is voor een applicatie zoals een rechtse seminearring voor een alternatief is”, en “Als je het argument van een applicatieve weggooit, krijg je een monoide, als je gooit weg het argument van een alternatief krijg je een RightSemiNearRing.”

Juiste semi-oorringen? “Hoe zijn de juiste semi-earrings erin terechtgekomen?” Ik hoor je huilen.Nou,

Definitie.Een rechts bijna semi-aring(ook rechts semi-nearring, maar de eerste lijkt meer te worden gebruikt op Google) is een viervoudige (R,+,·,0) waarbij (R,+,0) een monoïde is, (R,·) een halve groep is , en de volgende twee voorwaarden gelden:

  1. · is rechts-verdelend over +: voor alle r,s,tR, ( s+ t)r= sr+ tr.
  2. 0 is rechts absorberend voor ·: voor alle rR, 0r= 0.

Een links bijna-semiringwordt analoog gedefinieerd.

Dit werkt niet helemaal, omdat <*>niet echt associatief of een binaire operator is – de typen komen niet overeen. Ik denk dat dit is waar Edward Kmett op doelt als hij het heeft over “het argument weggooien”. Een andere optie zou kunnen zijn om te zeggen (ik weet niet zeker of dit juist is) dat we eigenlijk willen (f a, <|>, <*>, empty) om een ​​rechts bijna-semiringoïdete vormen, waarbij het achtervoegsel “-oid” aangeeft dat de binaire operatoren alleen kunnen worden toegepast op specifieke paren van elementen (à la groupoids). En we zouden ook willen zeggen dat (f a, <|>, <$>, empty) was een linker bijna-semiringoïde, hoewel dit mogelijk zou kunnen volgen uit de combinatie van de Applicativewetten en de rechter bijna-semiringoïde structuur. Maar nu raak ik tot over mijn oren in, en dit is sowieso niet erg relevant.

In ieder geval, deze wetten, die sterkerzijn dan de monoid-wetten, betekenen dat volkomen geldige Monoid-instanties ongeldige Alternative-instanties worden. Er zijn (minstens) twee voorbeelden hiervan in de standaardbibliotheek: Monoid a => (a,)en Maybe. Laten we ze allemaal snel bekijken.

Gezien twee monoïden, is hun product een monoïde; bijgevolg kunnen tupels op de voor de hand liggende manier een instantie van Monoidworden gemaakt (herformatteren van bron van het basispakket):

instance (Monoid a, Monoid b) => Monoid (a,b) where
  mempty = (mempty, mempty)
  (a1,b1) `mappend` (a2,b2) = (a1 `mappend` a2, b1 `mappend` b2)

Op dezelfde manier kunnen we tuples waarvan de eerste component een element van een monoïde is, maken in een instantie van Applicativedoor de monoïde elementen te accumuleren (herformatteren van bron van het basispakket):

instance Monoid a => Applicative ((,) a) where
  pure x = (mempty, x)
  (u, f) <*> (v, x) = (u `mappend` v, f x)

Tuples zijn echter geen instantie van Alternative, omdat ze dat niet kunnen zijn: de monoïdale structuur boven Monoid a => (a,b)is niet aanwezig voor alle typen b, en de monoïdale structuur van Alternativemoet agnostisch zijn van het innerlijke type. Niet alleen moet been monade zijn, om (f <> g) <*> a, we moeten de Monoidinstantie gebruiken voor functies, dit is voor functies van de vorm Monoid b => a -> b. En zelfs in het geval dat we alle noodzakelijke monoïdale structuur hebben, schendt het alle viervan de Alternativewetten. Om dit te zien, laat ssf n = (Sum n, (<> Sum n))en laat ssn = (Sum n, Sum n). Als we vervolgens (<>)voor mappendschrijven, krijgen we de volgende resultaten (die kunnen worden gecontroleerd in GHci, met af en toe een typeannotatie):

  1. Juiste verdeling:
    • (ssf 1 <> ssf 1) <*> ssn 1 = (Sum 3, Sum 4)
    • (ssf 1 <*> ssn 1) <> (ssf 1 <*> ssn 1) = (Sum 4, Sum 4)
  2. Juiste absorptie:
    • mempty <*> ssn 1 = (Sum 1, Sum 0)
    • mempty = (Sum 0, Sum 0)
  3. Linker distributiviteit:
    • (<> Sum 1) <$> (ssn 1 <> ssn 1) = (Sum 2, Sum 3)
    • ((<> Sum 1) <$> ssn 1) <> ((<> Sum 1) <$> ssn 1) = (Sum 2, Sum 4)
  4. Links absorptie:
    • (<> Sum 1) <$> mempty = (Sum 0, Sum 1)
    • mempty = (Sum 1, Sum 1)

Overweeg vervolgens Maybe. Zoals het er nu uitziet, Maybe‘s Monoiden Alternativeinstanties niet mee eens. (Hoewel de haskell-cafe discussieik vermeld op de begin van deze sectie stelt voor om dit te veranderen, er is een Optionnewtype uit het semigroup-pakketdat hetzelfde effect zou hebben.) Als een Monoid, stijgt Maybesemigroepeert in monoïden door Nothingals identiteit te gebruiken; aangezien het basispakket geen semigroup-klasse heeft, heft het alleen monoïden op, en dus krijgen we (herformatteren van bron van het basispakket):

instance Monoid a => Monoid (Maybe a) where
  mempty = Nothing
  Nothing `mappend` m       = m
  m       `mappend` Nothing = m
  Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)

Aan de andere kant, als een Alternative, vertegenwoordigt Maybede keuze met prioriteit bij mislukking, en dus krijgen we (opnieuw opnieuw formatteren van bron van het basispakket):

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

En het blijkt dat alleen de laatste voldoet aan de Alternativewetten. De instantie Monoidfaalt minder erg dan die van (,); het voldoetaan de wetten met betrekking tot <*>, hoewel bijna per ongeluk – het komt voort uit het gedrag van het enige exemplaar van Monoidvoor functies, die (zoals hierboven vermeld), functies opheft die monoiden teruggeven in de applicatieve functor van de lezer. Als je het uitwerkt (het is allemaal erg mechanisch), zul je zien dat de juiste distributiviteit en juiste absorptie voor <*>allemaal gelden voor zowel de Alternativeals Monoid-instanties, evenals linkerabsorptie voor fmap. En de linkerdistributiviteit voor fmapgeldt als volgt voor de Alternative-instantie:

f <$> (Nothing <|> b)
  = f <$> b                          by the definition of (<|>)
  = Nothing <|> (f <$> b)            by the definition of (<|>)
  = (f <$> Nothing) <|> (f <$> b)    by the definition of (<$>)
f <$> (Just a <|> b)
  = f <$> Just a                     by the definition of (<|>)
  = Just (f a)                       by the definition of (<$>)
  = Just (f a) <|> (f <$> b)         by the definition of (<|>)
  = (f <$> Just a) <|> (f <$> b)     by the definition of (<$>)

Het mislukt echter voor de instantie Monoid; bij het schrijven van (<>)voor mappend, hebben we:

  • (<> Sum 1) <$> (Just (Sum 0) <> Just (Sum 0)) = Just (Sum 1)
  • ((<> Sum 1) <$> Just (Sum 0)) <> ((<> Sum 1) <$> Just (Sum 0)) = Just (Sum 2)

Er is één kanttekening bij dit voorbeeld. Als u alleen wilt dat Alternatives compatibel zijn met <*>, en niet met <$>, dan Maybein orde. De dia’s van Edward Kmett, die hierboven zijn genoemd, verwijzen niet naar <$>, maar ik denk dat het redelijk lijkt om er ook wetten voor te eisen; desalniettemin kan ik niets vinden om me hierover te ondersteunen.

We kunnen dus concluderen dat een Alternativeeen sterkerevereiste is dan een Monoidte zijn, en dus een andere klasse vereist. Het zuiverste voorbeeld hiervan zou een type zijn met een inner-type agnostische Monoidinstantie en een Applicativeinstantie die incompatibel met elkaar waren; er zijn echter geen dergelijke typen in het basispakket en ik kan er geen bedenken. (Het is mogelijk dat er geen bestaat, hoewel het me zou verbazen.) Desalniettemin laten deze gnostische voorbeelden van het innerlijke type zien waarom de twee typeklassen verschillend moeten zijn.


Wat is het nut van de klasse van het type MonadPlus?

MonadPlusis, net als Alternative, een versterking van Monoid, maar met betrekking tot Monadin plaats van Applicative. Volgens Edward Kmett in zijn antwoordop de vraag “Onderscheid tussen typeklassen MonadPlus, Alternativeen Monoid?”, MonadPlusis ooksterker dan Alternative: de wet empty <*> abetekent bijvoorbeeld niet dat empty >>= f. AndrewCgeeft hier twee voorbeelden van: Maybeen het dubbele ervan. Het probleem wordt gecompliceerd door het feit dat er twee mogelijke reeksen wetten zijn voor MonadPlus. Er wordt algemeen aangenomen dat MonadPluseen monoïde moet vormen met mplusen mempty, en dat het moet voldoen aan de links nullaw, mempty >>= f = mempty. Sommige MonadPlusses voldoen echter aan de linkse verdeling, mplus a b >>= f = mplus (a >>= f) (b >>= f); en anderen voldoen aan left catch, mplus (return a) b = return a. (Merk op dat linker nul/verdeling voor MonadPlusanaloog zijn aan rechter verdeling/absorptie voor Alternative; (<*>)is meer analoog naar (=<<)dan (>>=).) Linkerverdeling is waarschijnlijk “beter”, dus elke MonadPlusinstantie die voldoet aan left catch, zoals Maybe, is een Alternativemaar niet de eerste soort MonadPlus. En aangezien left catch afhankelijk is van bestellen, kun je je een nieuwtype wrapper voorstellen voor Maybewaarvan de Alternative-instantie rechts-vooringenomen is in plaats van links -biased: a <|> Just b = Just b. Dit zal niet voldoen aan de linker distributie of left catch, maar zal een perfect geldige Alternativezijn.

Omdat echter van elk type dat iseen MonadPluszijn instantie moet samenvallen met zijn Alternativeinstantie (ik geloof dat dit vereist is in op dezelfde manier dat het vereist is dat apen (<*>)gelijk zijn voor Monaden die Applicatives), kun je je voorstellen dat je de klasse MonadPlusdefinieert als

class (Monad m, Alternative m) => MonadPlus' m

De klasse hoeft geen nieuwe functies te declareren; het is slechts een belofte over de wetten die worden nageleefd door emptyen (<|>)voor het gegeven type. Deze ontwerptechniek wordt niet gebruikt in de Haskell-standaardbibliotheken, maar wordt gebruikt in sommige meer wiskundig ingestelde pakketten voor vergelijkbare doeleinden; het pakket latticesgebruikt het bijvoorbeeld om het idee uit te drukken dat een roosteris slechts een voeg semilattice toeen een meet semilatticeover hetzelfde type die zijn verbonden door absorptiewetten.

De reden waarom u niet hetzelfde kunt doen voor Alternative, zelfs als u wilt garanderen dat Alternativeen Monoidaltijd samenvallen, is vanwege de soort mismatch. De gewenste klassenverklaring zou de vorm hebben

class (Applicative f, forall a. Monoid (f a)) => Alternative''' f

maar (zoals hierboven vermeld) ondersteunt zelfs GHC Haskell niet gekwantificeerde beperkingen.

Houd er rekening mee dat als Alternativeeen superklasse van MonadPlusis, Applicativeeen superklasse van Monad, dus veel geluk om dat te laten gebeuren. Als je dat probleem tegenkomt, is er altijd nog de WrappedMonadnewtype, dat elke Monadop de voor de hand liggende manier in een Applicativeverandert; er is een instance MonadPlus m => Alternative (WrappedMonad m) where ...dat precies doet wat je zou verwachten.


Antwoord 2, autoriteit 22%

import Data.Monoid
import Control.Applicative

Laten we een voorbeeld doornemen van hoe Monoid en Alternative samenwerken met de Maybefunctor en de ZipListfunctor, maar laten we helemaal opnieuw beginnen, deels om alle definities vers te krijgen in onze gedachten, deels om te voorkomen dat je de hele tijd van tabblad naar stukjes hackage moet overschakelen, maar vooral zodat ik voer dit verleden ghci uitom mijn typefouten te corrigeren!

(<>) :: Monoid a => a -> a -> a
(<>) = mappend -- I'll be using <> freely instead of `mappend`.

Hier is de Maybe-kloon:

data Perhaps a = Yes a | No  deriving (Eq, Show)

instance Functor Perhaps where
   fmap f (Yes a) = Yes (f a)
   fmap f No      = No

instance Applicative Perhaps where
   pure a = Yes a
   No    <*> _     = No
   _     <*> No    = No
   Yes f <*> Yes x = Yes (f x)

en nu ZipList:

data Zip a = Zip [a]  deriving (Eq,Show)

instance Functor Zip where
   fmap f (Zip xs) = Zip (map f xs)

instance Applicative Zip where
   Zip fs <*> Zip xs = Zip (zipWith id fs xs)   -- zip them up, applying the fs to the xs
   pure a = Zip (repeat a)   -- infinite so that when you zip with something, lengths don't change

Structuur 1: elementen combineren: monoide

Misschien klonen

Laten we eerst eens kijken naar Perhaps String. Er zijn twee manieren om ze te combineren. Ten eerste aaneenschakeling

(<++>) :: Perhaps String -> Perhaps String -> Perhaps String
Yes xs <++> Yes ys = Yes (xs ++ ys)
Yes xs <++> No     = Yes xs
No     <++> Yes ys = Yes ys
No     <++> No     = No

Aaneenschakeling werkt inherent op String-niveau, niet echt op Misschien-niveau, door Note behandelen alsof het Yes []is. Het is gelijk aan liftA2 (++). Het is verstandig en nuttig, maar misschien kunnen we veralgemenen van alleen ++naar het gebruik van elke manier van combineren – een willekeurige monoid dan!

(<++>) :: Monoid a => Perhaps a -> Perhaps a -> Perhaps a
Yes xs <++> Yes ys = Yes (xs `mappend` ys)
Yes xs <++> No     = Yes xs
No     <++> Yes ys = Yes ys
No     <++> No     = No

Deze monoïde structuur voor Maybeprobeert zoveel mogelijk te werken op het niveau van a. Let op de beperking Monoid a, die ons vertelt dat we structuur gebruiken vanaf het niveau a. Dit is geen alternatieve structuur, het is een afgeleide (opgeheven) monoide structuur.

instance Monoid a => Monoid (Perhaps a) where
   mappend = (<++>)
   mempty = No

Hier heb ik de structuur van de gegevens a gebruikt om structuur aan het geheel toe te voegen. Als ik Sets zou combineren, zou ik in plaats daarvan een Ord a-context kunnen toevoegen.

ZipList-kloon

Dus hoe moeten we elementencombineren met een zipList? Waar moeten deze naar toe als we ze combineren?

  Zip ["HELLO","MUM","HOW","ARE","YOU?"] 
<> Zip ["this", "is", "fun"]
=  Zip ["HELLO" ? "this",   "MUM" ? "is",   "HOW" ? "fun"]
mempty = ["","","","",..]   -- sensible zero element for zipping with ?

Maar wat moeten we gebruiken voor ?. Ik zeg dat de enige verstandige keuze hier ++is. Eigenlijk, voor lijsten, (<>) = (++)

  Zip [Just 1,  Nothing, Just 3, Just 4]
<> Zip [Just 40, Just 70, Nothing]
 =  Zip [Just 1 ? Just 40,    Nothing ? Just 70,    Just 3 ? Nothing]
mempty = [Nothing, Nothing, Nothing, .....]  -- sensible zero element

Maar wat kunnen we gebruiken voor ?Ik zeg dat het de bedoeling is dat we elementen combineren, dus moeten we de operator voor het combineren van elementen van Monoid opnieuw gebruiken: <>.

instance Monoid a => Monoid (Zip a) where
   Zip as `mappend` Zip bs = Zip (zipWith (<>) as bs) -- zipWith the internal mappend
   mempty = Zip (repeat mempty)  -- repeat the internal mempty

Dit is de enige verstandige manier om de elementen te combineren met een zip – dus het is de enige verstandige monoïde instantie.

Interessant is dat dat niet werkt voor het bovenstaande Maybe-voorbeeld, omdat Haskell niet weet hoe hij Ints moet combineren – moet het +of *? Om een ​​Monoid-instantie op numerieke gegevens te krijgen, plaatst u ze in Sumof Productom te vertellen welke monoïde moet worden gebruikt.

Zip [Just (Sum 1),   Nothing,       Just (Sum 3), Just (Sum 4)] <> 
Zip [Just (Sum 40),  Just (Sum 70), Nothing]
= Zip [Just (Sum 41),Just (Sum 70), Just (Sum 3)]
   Zip [Product 5,Product 10,Product 15] 
<> Zip [Product 3, Product 4]
 =  Zip [Product 15,Product 40]

Belangrijk punt

Merk ophet feit dat het type in een Monoid soort *heeft, is precies wat ons in staat stelt om de Monoid acontext hier te plaatsen – wij zou ook Eq aof Ord akunnen toevoegen. In een monoide zijn de ruwe elementen van belang. Een Monoid-instantie is ontworpenom u de gegevens binnen de structuur te laten manipuleren en combineren.

Structuur 2: keuze op hoger niveau: alternatief

Een keuze-operator is vergelijkbaar, maar ook anders.

Misschien klonen

(<||>) :: Perhaps String -> Perhaps String -> Perhaps String
Yes xs <||> Yes ys = Yes xs   -- if we can have both, choose the left one
Yes xs <||> No     = Yes xs
No     <||> Yes ys = Yes ys
No     <||> No     = No  

Hier is geen aaneenschakeling– we hebben ++helemaal niet gebruikt – deze combinatie werkt puur op het niveau Maybe, dus laten we verander de typesignatuur in

(<||>) :: Perhaps a -> Perhaps a -> Perhaps a
Yes xs <||> Yes ys = Yes xs   -- if we can have both, choose the left one
Yes xs <||> No     = Yes xs
No     <||> Yes ys = Yes ys
No     <||> No     = No  

Let op: er is geen beperking – we gebruiken niet de structuur van het a-niveau, alleen de structuur op het Maybe-niveau. Dit is een alternatieve structuur.

instance Alternative Perhaps where
   (<|>) = (<||>)
   empty = No  

ZipList-kloon

Hoe moeten we kiezen tussen twee ziplists?

Zip [1,3,4] <|> Zip [10,20,30,40] = ????

Het zou erg verleidelijk zijn om <|>te gebruiken voor de elementen, maar dat kunnen we niet omdat het type elementen niet voor ons beschikbaar is. Laten we beginnen met de empty. Het kan geen element gebruiken omdat we het type van de elementen niet weten bij het definiëren van een alternatief, dus het moet Zip []zijn. We hebben een linker (en bij voorkeur rechtse) identiteit nodig voor <|>, dus

Zip [] <|> Zip ys = Zip ys
Zip xs <|> Zip [] = Zip xs

Er zijn twee verstandige keuzes voor Zip [1,3,4] <|> Zip [10,20,30,40]:

  1. Zip [1,3,4]omdat dit de eerste is – consistent met Misschien
  2. Zip [10,20,30,40]omdat deze het langst is – consistent met Zip []wordt weggegooid

Nou, dat is gemakkelijk te beslissen: aangezien pure x = Zip (repeat x), kunnen beide lijsten oneindig zijn, dus het vergelijken van de lengte kan nooit eindigen, dus het moet de eerste zijn . Dus de enige verstandige alternatieve instantie is:

instance Alternative Zip where
   empty = Zip []
   Zip [] <|> x = x
   Zip xs <|> _ = Zip xs

Dit is het enige verstandige alternatief dat we hadden kunnen definiëren. Merk op hoe anders het is dan de Monoid-instantie, omdat we niet met de elementen konden knoeien, we konden er niet eens naar kijken.

Belangrijkste punt

Merk opdat, omdat Alternativeeen soort constructor * -> *er is geen mogelijkheidom een ​​Ord aof Eq aof Monoid acontext toe te voegen . Een Alternatief is niet toegestaanom informatie over de gegevens binnen de structuur te gebruiken. U kunt, hoe graag u ook zou willen, niets met de gegevens doen, behalve ze mogelijk weggooien.

Belangrijk punt: Wat is het verschil tussen Alternative en Monoid?

Niet veel – het zijn beide monoïden, maar om de laatste twee secties samen te vatten:

Monoid *-instanties maken het mogelijk om interne gegevens te combineren. Alternative (* -> *)instanties maken het onmogelijk. Monoid biedt flexibiliteit, Alternative biedt garanties. De soorten *en (* -> *)zijn de belangrijkste drijfveren van dit verschil. Als je ze beide hebt, kun je beide soorten bewerkingen gebruiken.

Dit is het juiste, en onze twee smaken zijn beide geschikt. De Monoid-instantie voor Perhaps Stringstaat voor het samenvoegen van alle tekens, de Alternative-instantie voor een keuze tussen Strings.

Er is niets mis met de Monoid-instantie voor Maybe – het doet zijn werk, het combinerenvan gegevens.
Er is niets mis met de Alternative-instantie voor Maybe – het doet zijn werk, kiezentussen dingen.

De Monoid-instantie voor Zip combineert zijn elementen. De alternatieve instantie voor Zip wordt gedwongen een van de lijsten te kiezen – de eerste niet-lege.

Het is goed om beide te kunnen doen.

Waarvoor dient de applicatieve context?

Er is enige interactie tussen kiezen en solliciteren. Zie Antal SZ’ s wetten vermeld in zijn vraagof in het midden van zijn antwoord hier.

Vanuit praktisch oogpunt is het handig omdat Alternative iets is dat door sommige applicatieve functors wordt gebruikt om te kiezen. De functionaliteit werd gebruikt voor Applications, en dus werd een algemene interfaceklasse uitgevonden. Applicatieve functors zijn goed voor het weergeven van berekeningen die waarden produceren (IO, Parser, Input UI-element,…) en sommige ervan moeten fouten afhandelen – er is een alternatief nodig.

Waarom heeft Alternative empty?

waarom heeft Alternative een lege methode/lid nodig? Ik kan het mis hebben, maar het lijkt helemaal niet te worden gebruikt … tenminste in de code die ik kon vinden. En het lijkt niet te passen bij het thema van de klas — als ik twee dingen heb en er één moet kiezen, waar heb ik dan een ‘leeg’ voor nodig?

Dat is hetzelfde als vragen waarom toevoeging een 0 nodig heeft – als je dingen wilt toevoegen, wat heeft het dan voor zin om iets te hebben dat niets toevoegt? Het antwoord is dat 0 het cruciale scharniergetal is waar alles bovendien om draait, net zoals 1 cruciaal is voor vermenigvuldiging, []cruciaal is voor lijsten (en y=e^xis cruciaal voor calculus). Praktisch gezien gebruik je deze doe-niets-elementen om je gebouw te starten:

sum = foldr (+) 0
concat = foldr (++) []
msum = foldr (`mappend`) mempty          -- any Monoid
whichEverWorksFirst = foldr (<|>) empty  -- any Alternative

Kunnen we MonadPlus niet vervangen door Monad+Alternative?

wat is het nut van de MonadPlus-typeklasse? Kan ik niet al zijn goedheid ontsluiten door iets te gebruiken als zowel een monade als alternatief? Waarom niet gewoon dumpen? (Ik weet zeker dat ik het mis heb, maar ik heb geen tegenvoorbeelden)

Je hebt het mis, er zijn geen tegenvoorbeelden!

Uw interessante vraag heeft ertoe geleid dat Antal S-Z, Petr Pudlák en ik zich hebben verdiept in wat de relatie tussen MonadPlus en Applicative werkelijk is. Het antwoord,
hier
en hier
is dat alles wat een MonadPlusis (in de linker distributiezin – volg links voor details) ook een Alternativeis, maar niet andersom.

Dit betekent dat als u een instantie van Monad en MonadPlus maakt, deze voldoet sowieso aan de voorwaarden voor Aanvragend en Alternatief. Dit betekent dat als je de regels voor MonadPlus volgt (met linker dist), je net zo goed van je Monad een Applicatie hebt gemaakt en Alternatief hebt gebruikt.

Als we echter de MonadPlus-klasse verwijderen, verwijderen we een verstandige plek om de regels te documenteren, en verlies je de mogelijkheid om te specificeren dat iets Alternatief is zonder MonadPlus te zijn (wat we technisch gezien hadden moeten doen voor Maybe). Dit zijn theoretische redenen. De praktische reden is dat het bestaande code zou breken. (Dat is ook de reden waarom noch Applicative noch Functor superklassen zijn van Monad.)

Zijn Alternative en Monoid niet hetzelfde? Zijn Alternative en Monoid niet totaal verschillend?

de ‘pedia zegt dat “de klasse Alternative is voor Applicatieve functors die ook een monoïde structuur hebben.” Ik snap dit niet — betekent Alternative niet iets totaal anders dan Monoid? d.w.z. ik begreep het punt van de klasse Alternative als kiezen tussen twee dingen, terwijl ik begreep dat Monoids ging over het combineren van dingen.

Monoid en Alternative zijn twee manieren om op een verstandige manier één object uit twee te halen. Wiskunde maakt het niet uit of je je gegevens kiest, combineert, mixt of opblaast, daarom werd Alternative een Monoid for Applicative genoemd. Je lijkt nu thuis te zijn met dat concept, maar je zegt nu

voor typen die zowel een Alternative als een Monoid-instantie hebben, zijn de instanties bedoeld hetzelfde te zijn

Ik ben het hier niet mee eens, en ik denk dat mijn Maybe- en ZipList-voorbeelden zorgvuldig zijn uitgelegd waarom ze anders zijn. Als er iets is, denk ik dat het zeldzaam zou moeten zijn dat ze hetzelfde zijn. Ik kan maar één voorbeeld bedenken, duidelijke lijsten, waar dit van toepassing is. Dat komt omdat lijsten een fundamenteel voorbeeld zijn van een monoïde met ++, maar ook lijsten worden in sommige contexten gebruikt als een onbepaalde keuze van elementen, dus <|>zou moeten ook ++zijn.


Antwoord 3, autoriteit 9%

Samenvatting

  • We moeten (instanties die dezelfde bewerkingen bieden als) Monoïde-instanties definiëren voor sommige applicatieve functors, die echt combineren op het applicatieve functorniveau, en niet alleen monooïden op een lager niveau optillen. De voorbeeldfout hieronder van litvar = liftA2 mappend literal variablelaat zien dat <|>in het algemeen niet kan worden gedefinieerd als liftA2 mappend; <|>werkt in dit geval door parsers te combineren, niet hun gegevens.

  • Als we Monoid rechtstreeks zouden gebruiken, zouden we taalextensies nodig hebben om de instanties te definiëren. Alternativeis van hogere kwaliteit, zodat u deze instanties kunt maken zonder taalextensies.

Voorbeeld: Parsers

Stel je voor dat we enkele declaraties ontleden, dus we importeren alles wat we nodig hebben

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

en denk na over hoe we een type gaan ontleden. We kiezen voor simplistisch:

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

Laten we nu een parser schrijven voor letterlijke typen:

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

Betekenis: ontleden een upperhoofdletter, dan many alphaNumeric tekens, combineer de resultaten in een enkele string met de pure functie (:). Pas daarna de pure functie Literaltoe om die Strings om te zetten in Types. We zullen variabele typen op precies dezelfde manier ontleden, behalve dat we beginnen met een lowerhoofdletter:

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

Dat is geweldig, en parseTest literal "Bool" == Literal "Bool"precies zoals we hadden gehoopt.

Vraag 3a: Als het is om de effecten van applicative te combineren met het gedrag van Monoid, waarom dan niet gewoon liftA2 mappend

Bewerken:Oeps – vergeten <|>daadwerkelijk te gebruiken!
Laten we nu deze twee parsers combineren met Alternative:

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

Dit kan elk type ontleden: parseTest types "Int" == Literal "Bool"en parseTest types "a" == Variable "a".
Dit combineert de twee parsers, niet de twee waarden. In die zin werkt het op het niveau van de toepassingsfunctie in plaats van op het gegevensniveau.

Als we het echter proberen:

litvar = liftA2 mappend literal variable

dat zou de compiler vragen om de twee waardendie ze genereren, op gegevensniveau te combineren.
We krijgen

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

Dus we kwamen het eerste te weten; de klasse Alternative doet iets heel anders dan liftA2 mappend, omdat het objecten op een ander niveau combineert – het combineert de parsers, niet de geparseerde gegevens. Als je het op deze manier wilt zien, is het een combinatie op een echt hoger niveau, niet alleen een lift. Ik hou er niet van om het zo te zeggen, omdat Parser Typesoort *heeft, maar het is waar om te zeggen dat we de Parsercombineren s, niet de Types.

(Zelfs voor typen met een Monoid-instantie geeft liftA2 mappendu niet dezelfde parser als <|>. Als u het probeert op Parser Stringje krijgt liftA2 mappenddie de een na de ander parseert en vervolgens samenvoegt, versus <|>die de eerste parser en standaard zal proberen naar de tweede als het mislukt.)

Vraag 3b: Op welke manier werkt de <|> :: f a -> f a -> f averschillen van Monoid’s mappend :: b -> b -> b?

Ten eerste heb je gelijk als je opmerkt dat het geen nieuwe functionaliteit biedt via een Monoid-instantie.

Ten tweede is er echter een probleem met het rechtstreeks gebruiken van Monoid:
Laten we proberen mappendop parsers te gebruiken, terwijl we tegelijkertijd laten zien dat het dezelfde structuur heeft als Alternative:

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

Oeps! We krijgen

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)'

Dus als je een applicatieve functor fhebt, laat de instantie Alternativezien dat f aeen monoïde is, maar je zou dat alleen kunnen declareren als een Monoidmet een taalextensie.

Zodra we { -# LANGUAGE TypeSynonymInstances #- }bovenaan het bestand hebben toegevoegd, gaat het goed en kunnen we definiëren

typeParser = literal `mappend` variable

en tot onze vreugde werkt het: parseTest typeParser "Yes" == Literal "Yes"en parseTest typeParser "a" == Literal "a".

Zelfs als je geen synoniemen hebt (Parseren Stringzijn synoniemen, dus ze zijn uit), heb je nog steeds { -# LANGUAGE FlexibleInstances #- }om een ​​instantie zoals deze te definiëren:

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)

(De monoïde instantie voor Maybe omzeilt dit door de onderliggende monoïde op te tillen.)

Een standaardbibliotheek onnodig afhankelijk maken van taalextensies is duidelijk onwenselijk.


Dus daar heb je het. Alternative is gewoon monoid voor applicatieve functors (en is niet zomaar een lift van een monoid). Het heeft het hogere type f a -> f a -> f azodat u er een kunt definiëren zonder taalextensies.

Uw andere vragen, voor de volledigheid:

  1. Waarom heeft Alternative een lege methode/lid nodig?
    Omdat het hebben van een identiteit voor een operatie soms handig is.
    U kunt bijvoorbeeld anyA = foldr (<|>) emptydefiniëren zonder vervelende randgevallen te gebruiken.

  2. wat is het nut van de MonadPlus-typeklasse? Kan ik niet al zijn goedheid ontsluiten door iets te gebruiken als zowel een monade als alternatief?
    Nee. Ik verwijs je terug naar de vraag waarnaar je hebt gelinkt:

Bovendien, zelfs als Applicative een superklasse van Monad zou zijn, zou je uiteindelijk toch de MonadPlus-klasse nodig hebben, omdat het gehoorzamen van empty <*> m = emptyis niet strikt genoeg om te bewijzen dat empty >>= f = empty.

….en ik heb een voorbeeld bedacht: Misschien. Ik leg het in detail uit, met bewijs in dit antwoordop de vraag van Antal. Voor de doeleinden van dit antwoord is het vermeldenswaard dat ik >>= heb kunnen gebruiken om de MonadPlus-instantie te maken die de alternatieve wetten overtrad.


Monoïde structuur is handig. Alternatief is de beste manier om het te bieden aan toepassingsfuncties.


Antwoord 4, autoriteit 4%

Ik zal MonadPlus niet behandelen omdat er onenigheid is over de wetten.


Na te hebben geprobeerd en geen zinvolle voorbeelden te vinden waarin de structuur van een Applicatief op natuurlijke wijze leidt tot een alternatieve instantie die het niet eens is met de Monoid-instantie*, kwam ik uiteindelijk op dit:

De wetten van Alternative zijn strenger dan die van Monoid, omdat het resultaat nietkan afhangen van het innerlijke type. Dit sluit een groot aantal Monoid-instanties uit als alternatieven.
Deze datatypes laten gedeeltelijke (wat betekent dat ze alleen werken voor sommige innerlijke typen) Monoid-instanties toe die verboden zijn door de extra ‘structuur’ van de * -> *soort. Voorbeelden:

  • de standaard Maybe-instantie voor Monoid gaat ervan uit dat het binnenste type Monoid => geen alternatief

  • ZipLists, tupels en functies kunnen allemaal Monoids worden gemaakt, alshun innerlijke typen Monooids => geen alternatieven

  • reeksen die ten minste één element hebben — kunnen geen alternatieven zijn omdat er geen emptyis:

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

Aan de andere kant kunnen sommige gegevenstypen geen alternatieven worden gemaakt omdat ze *-achtig zijn:

  • eenheid — ()
  • Ordering
  • cijfers, booleans

Mijn afgeleide conclusie: voor typen die zowel een Alternative als een Monoid-instantie hebben, zijn de instanties bedoeld hetzelfde te zijn.Zie ook dit antwoord.


exclusief Misschien, wat volgens mij niet telt omdat de standaardinstantie Monoid niet zou moeten vereisen voor het innerlijke type, in welk geval het identiek zou zijn aan Alternative


Antwoord 5, autoriteit 2%

Ik begreep het punt van de klasse Alternative als het kiezen tussen twee dingen, terwijl ik begreep dat Monoids ging over het combineren van dingen.

Als je hier even over nadenkt, zijn ze hetzelfde.

De +combineert dingen (meestal cijfers), en zijn typehandtekening is Int -> Int -> Int(of wat dan ook).

De operator <|>selecteert tussen alternatieven, en zijn typesignatuur is ook hetzelfde: neem twee overeenkomende dingen en retourneer een gecombineerd ding.

Other episodes