Wie spricht man diese Funktionen in der Anwendungsklasse aus?
(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
(Das heißt, wenn sie keine Operatoren wären, wie könnten sie genannt werden?)
Als Randnotiz, wenn Sie pure
in etwas umbenennen könnten, das für Nicht-Mathematiker freundlicher ist, wie würden Sie es nennen?
pure
könnte seinmakeApplicative
.pure
Vorschlag als Antwort und ich werde dich positiv bewertenAntworten:
Ihre Mathematik zu kennen oder nicht, ist hier weitgehend irrelevant, denke ich. Wie Sie wahrscheinlich wissen, leiht sich Haskell ein paar Begriffe aus verschiedenen Bereichen der abstrakten Mathematik, insbesondere der Kategorietheorie , aus denen wir Funktoren und Monaden erhalten. Die Verwendung dieser Begriffe in Haskell weicht etwas von den formalen mathematischen Definitionen ab, aber sie sind normalerweise nahe genug, um ohnehin gute beschreibende Begriffe zu sein.
Die
Applicative
Typklasse liegt irgendwo zwischenFunctor
undMonad
, daher würde man erwarten, dass sie eine ähnliche mathematische Basis hat. Die Dokumentation zumControl.Applicative
Modul beginnt mit:Hmm.
Nicht ganz so eingängig wie
Monad
, denke ich.Im Grunde
Applicative
läuft alles darauf hinaus, dass es keinem mathematisch besonders interessanten Konzept entspricht. Es liegen also keine vorgefertigten Begriffe herum, die die Art und Weise erfassen, wie es in Haskell verwendet wird. Legen Sie also die Mathematik vorerst beiseite.Wenn wir wissen wollen, wie wir es nennen sollen, kann
(<*>)
es hilfreich sein zu wissen, was es im Grunde bedeutet.Also, was ist überhaupt los
Applicative
und warum nennen wir es so?Was
Applicative
in der Praxis bedeutet, ist eine Möglichkeit, beliebige Funktionen in a zu hebenFunctor
. Betrachten Sie die Kombination vonMaybe
(wohl der einfachste nicht trivialeFunctor
) undBool
(ebenfalls der einfachste nicht triviale Datentyp).Mit dieser Funktion
fmap
können wirnot
von der ArbeitBool
zur Arbeit übergehenMaybe Bool
. Aber was ist, wenn wir heben wollen(&&)
?Nun, das wollen wir überhaupt nicht ! In der Tat ist es so ziemlich nutzlos. Wir können versuchen , schlau zu sein und heimlich eine andere
Bool
inMaybe
durch die Hintertür ...... aber das ist nicht gut. Zum einen ist es falsch. Zum anderen ist es hässlich . Wir könnten es weiter versuchen, aber es stellt sich heraus, dass es keine Möglichkeit gibt, eine Funktion mehrerer Argumente aufzuheben, um an einer beliebigen zu arbeiten
Functor
. Nervig!Auf der anderen Seite konnten wir es einfach tun , wenn wir gebraucht
Maybe
‚sMonad
Beispiel:Das ist eine Menge Aufwand, nur um eine einfache Funktion zu übersetzen - weshalb
Control.Monad
eine Funktion bereitgestellt wird, um dies automatisch zu tunliftM2
. Die 2 in ihrem Namen bezieht sich auf die Tatsache, dass sie mit Funktionen von genau zwei Argumenten arbeitet; Ähnliche Funktionen gibt es für 3, 4 und 5 Argumentfunktionen. Diese Funktionen sind besser , aber nicht perfekt, und die Angabe der Anzahl der Argumente ist hässlich und ungeschickt.Das bringt uns zu dem Artikel, in dem die Typklasse "Anwendbar" eingeführt wurde . Darin machen die Autoren im Wesentlichen zwei Beobachtungen:
Functor
ist eine sehr natürliche SacheMonad
Die Anwendung mit normalen Funktionen wird durch einfaches Nebeneinander von Begriffen geschrieben. Um die "angehobene Anwendung" so einfach und natürlich wie möglich zu gestalten, werden Infix-Operatoren vorgestellt, die für die Anwendung stehen, in die
Functor
und eine Typklasse gehoben werden , um das bereitzustellen, was dafür benötigt wird .All dies bringt uns zu folgendem Punkt:
(<*>)
Stellt einfach die Funktionsanwendung dar - warum sollte man sie also anders aussprechen als den Leerzeichen-Operator "Nebeneinander"?Aber wenn das nicht sehr befriedigend ist, können wir beobachten, dass das
Control.Monad
Modul auch eine Funktion bietet, die dasselbe für Monaden tut:Wo
ap
ist natürlich die Abkürzung für "bewerben". Da jedes seinMonad
kannApplicative
undap
nur die Teilmenge der in letzterem vorhandenen Merkmale benötigt, können wir vielleicht sagen, dass es aufgerufen werden sollte , wenn(<*>)
es kein Operator wäreap
.Wir können uns den Dingen auch aus der anderen Richtung nähern. Der
Functor
Hebevorgang wird aufgerufen,fmap
weil er eine Verallgemeinerung desmap
Vorgangs auf Listen darstellt. Welche Art von Funktion auf Listen würde funktionieren(<*>)
? Da ist wasap
gibt natürlich das, auf Listen steht, aber das allein ist nicht besonders nützlich.In der Tat gibt es eine vielleicht natürlichere Interpretation für Listen. Was fällt Ihnen ein, wenn Sie sich die folgende Typensignatur ansehen?
Die Idee, die Listen parallel aufzustellen und jede Funktion in der ersten auf das entsprechende Element der zweiten anzuwenden, ist einfach so verlockend. Unglücklicherweise für unseren alten Freund verstößt
Monad
diese einfache Operation gegen die Monadengesetze, wenn die Listen unterschiedlich lang sind. Aber es macht eine GeldstrafeApplicative
, in welchem Fall(<*>)
wird eine Möglichkeit, eine verallgemeinerte Version von aneinander zu reihenzipWith
, also können wir uns vielleicht vorstellen, es zu nennenfzipWith
?Diese Zipping-Idee schließt den Kreis. Erinnern Sie sich an das Mathe-Zeug früher über monoidale Funktoren? Wie der Name schon sagt, können Sie auf diese Weise die Struktur von Monoiden und Funktoren kombinieren, die beide bekannte Haskell-Typklassen sind:
Wie würden diese aussehen, wenn Sie sie in eine Schachtel legen und ein wenig aufrütteln würden? Von
Functor
behalten wir die Idee einer Struktur unabhängig von ihrem Typparameter und vonMonoid
behalten wir die Gesamtform der Funktionen bei:Wir wollen nicht davon ausgehen, dass es eine Möglichkeit gibt, ein wirklich "leeres" zu erstellen
Functor
, und wir können keinen Wert eines beliebigen Typs heraufbeschwören, also werden wir den Typ vonmfEmpty
as festlegenf ()
.Wir möchten auch nicht erzwingen
mfAppend
, dass ein konsistenter Typparameter benötigt wird. Jetzt haben wir Folgendes:Wofür ist der Ergebnistyp
mfAppend
? Wir haben zwei beliebige Typen, von denen wir nichts wissen, daher haben wir nicht viele Optionen. Am sinnvollsten ist es, einfach beides zu behalten:An diesem Punkt
mfAppend
ist jetzt eindeutig eine verallgemeinerte Version vonzip
On-Listen, und wir könnenApplicative
leicht rekonstruieren :Dies zeigt uns auch, dass dies
pure
mit dem Identitätselement von a zusammenhängtMonoid
, sodass andere gute Namen dafür alles sein können, was auf einen Einheitswert, eine Nulloperation oder dergleichen hindeutet.Das war langwierig, um es zusammenzufassen:
(<*>)
ist nur eine modifizierte Funktionsanwendung, sodass Sie sie entweder als "ap" oder "apply" lesen oder ganz wie bei einer normalen Funktionsanwendung entfernen können.(<*>)
Verallgemeinert auch grobzipWith
auf Listen, so dass Sie es als "Zip-Funktoren mit" lesen können, ähnlich wiefmap
als "Karte eines Funktors mit".Die erste ist näher an der Absicht der
Applicative
Typklasse - wie der Name schon sagt - und das empfehle ich.Tatsächlich ermutige ich die liberale Verwendung und Nichtaussprache aller aufgehobenen Anwendungsbetreiber :
(<$>)
, wodurch eine Einzelargumentfunktion in eineFunctor
(<*>)
, die eine Multi-Argument-Funktion durch eine verkettetApplicative
(=<<)
, die eine Funktion bindet, die a in eineMonad
vorhandene Berechnung eingibtAlle drei sind im Kern nur reguläre Funktionsanwendungen, die ein wenig aufgewertet wurden.
quelle
Applicative
der funktionale, idiomatische Stil, den er fördert, nicht genug Liebe findet, daher konnte ich der Chance nicht widerstehen, seine Tugenden ein wenig zu preisen, um zu erklären, wie ich (nicht) ausspreche(<*>)
.Applicative
's! So etwas wie[| f a b c d |]
(wie vom Originalpapier vorgeschlagen). Dann würden wir den<*>
Kombinator nicht brauchen und Sie würden einen solchen Ausdruck als Beispiel für "Funktionsanwendung in einem Funktionskontext" bezeichnenMonad
. OderFunctor
oderMonoid
oder irgendetwas anderes, das einen gut etablierten Begriff hat, der weniger als drei Adjektive umfasst. "Anwendbar" ist lediglich ein wenig inspirierender, wenn auch einigermaßen beschreibender Name, der auf etwas geklatscht ist, das eher einen benötigt.Da ich keine Ambitionen habe, die technische Antwort von CA McCann zu verbessern , werde ich mich mit der flauschigeren befassen:
Als Alternative schlage ich einen anderen Namen vor , zumal die ständige Angst und der Verrat, die gegen die
Monad
Version "return
" geweint werden, kein Ende haben , ein anderes Ende haben, das seine Funktion auf eine Weise vorschlägt, die den zwingendsten imperativen Programmierern gerecht wird und das funktionalste von ... nun, hoffentlich kann sich jeder gleich beschweren über :inject
.Nimm einen Wert. "Injizieren" Sie es in die
Functor
,Applicative
,Monad
oder was-haben-Sie. Ich stimme für "inject
" und habe diese Nachricht genehmigt.quelle
inject
ist ein ausgezeichneter Name und wahrscheinlich besser als meiner, obwohl als kleine Randnotiz "injizieren" in - glaube ich - Smalltalk und Ruby für eine Art Linksfalte verwendet wird. Ich habe diese Wahl des Namens jedoch nie verstanden ...inject
in Ruby & Smalltalk verwendet wird, weil es so ist, als würden Sie einen Operator zwischen jedes Element in der Liste "einfügen". Zumindest habe ich so immer daran gedacht.foldr
. (Sie ersetzen(:)
und[]
, wobei(:)
2 Argumente benötigt werden und[]
eine Konstante sind, daherfoldr (+) 0 (1:2:3:[])
↝1+2+3+0
.)Bool
Es ist nurif
-then
-else
(zwei Konstanten, wählen Sie eine aus) und dafürMaybe
heißt esmaybe
... Haskell hat keinen einzigen Namen / keine einzige Funktion dafür, da alle unterschiedliche Typen haben (im Allgemeinen ist elim nur Rekursion / Induktion)In Kürze:
<*>
Sie können es anwenden nennen . SoMaybe f <*> Maybe a
kann ausgesprochen werden , da geltenMaybe f
überMaybe a
.Sie könnten umbenennen
pure
zuof
, wie viele JavaScript - Bibliotheken zu tun. In JS können Sie einMaybe
mit erstellenMaybe.of(a)
.Außerdem hat Haskells Wiki eine Seite auf die Aussprache der Sprache Operatoren hier
quelle
Quelle: Haskell Programming from First Principles von Chris Allen und Julie Moronuki
quelle