Ich bin neu in Haskell und lese über Funktoren und anwendungsbezogene Funktoren. Ok, ich verstehe Funktoren und wie ich sie verwenden kann, aber ich verstehe nicht, warum anwendbare Funktoren nützlich sind und wie ich sie in Haskell verwenden kann. Können Sie mir anhand eines einfachen Beispiels erklären, warum ich anwendbare Funktoren benötige?
78
Antworten:
Anwendbare Funktoren sind eine Konstruktion, die den Mittelpunkt zwischen Funktoren und Monaden bildet und daher weiter verbreitet ist als Monaden, während sie nützlicher als Funktoren ist. Normalerweise können Sie eine Funktion einfach über einen Funktor abbilden. Mit anwendbaren Funktoren können Sie eine "normale" Funktion (unter Verwendung nicht funktionaler Argumente) verwenden, um mehrere Werte zu bearbeiten, die sich in Funktorkontexten befinden. Als Konsequenz erhalten Sie so eine effektive Programmierung ohne Monaden.
Eine nette, in sich geschlossene Erklärung voller Beispiele finden Sie hier . Sie können auch ein praktisches Parsing-Beispiel lesen, das von Bryan O'Sullivan entwickelt wurde und für das keine Vorkenntnisse erforderlich sind.
quelle
Anwendbare Funktoren sind nützlich, wenn Sie eine Sequenzierung von Aktionen benötigen, aber keine Zwischenergebnisse benennen müssen. Sie sind daher schwächer als Monaden, aber stärker als Funktoren (sie haben keinen expliziten Bindungsoperator, erlauben jedoch die Ausführung beliebiger Funktionen innerhalb des Funktors).
Wann sind sie nützlich? Ein häufiges Beispiel ist das Parsen, bei dem Sie eine Reihe von Aktionen ausführen müssen, die Teile einer Datenstruktur der Reihe nach lesen und dann alle Ergebnisse zusammenfügen. Dies ist wie eine allgemeine Form der Funktionszusammensetzung:
f a b c d
wo Sie sich vorstellen können
a
,b
und so weiter als willkürliche Aktionen, die ausgeführt werden sollen, undf
als Funktor, der auf das Ergebnis angewendet werden soll .f <$> a <*> b <*> c <*> d
Ich stelle sie mir gerne als überladenes "Leerzeichen" vor. Oder dass reguläre Haskell-Funktionen im Identitätsanwendungs-Funktor enthalten sind.
Siehe " Anwendbare Programmierung mit Effekten "
quelle
Conor McBride und Ross Patersons Functional Pearl über den Stil haben mehrere gute Beispiele. Es ist auch in erster Linie für die Popularisierung des Stils verantwortlich. Sie verwenden den Begriff "Redewendung" für "Anwendungsfunktor", aber ansonsten ist er ziemlich verständlich.
quelle
Es ist schwierig, Beispiele zu finden, bei denen Sie anwendbare Funktoren benötigen . Ich kann verstehen, warum sich ein fortgeschrittener Haskell-Programmierer diese Frage selbst stellen würde, da die meisten Einführungstexte Instanzen darstellen, die von Monaden abgeleitet sind und Applicative Functors nur als bequeme Schnittstelle verwenden.
Wie hier und in den meisten Einführungen zum Thema erwähnt, besteht die wichtigste Erkenntnis darin, dass sich anwendbare Funktoren zwischen Funktoren und Monaden befinden (sogar zwischen Funktoren und Pfeilen). Alle Monaden sind anwendbare Funktoren, aber nicht alle Funktoren sind anwendbar.
Daher müssen wir manchmal manchmal anwendbare Kombinatoren für etwas verwenden, für das wir keine monadischen Kombinatoren verwenden können. Eine solche Sache ist
ZipList
(siehe auch diese SO-Frage für einige Details ), die nur ein Wrapper um Listen ist, um eine andere anwendbare Instanz zu haben als die, die von der Monad-Instanz der Liste abgeleitet ist. In der anwendbaren Dokumentation wird die folgende Zeile verwendet, um eine intuitive Vorstellung davon zu geben, wofürZipList
:f <$> ZipList xs1 <*> ... <*> ZipList xsn = ZipList (zipWithn f xs1 ... xsn)
Wie hier erwähnt , ist es möglich, eigenartige Monad-Instanzen zu erstellen, die fast für ZipList funktionieren.
Es gibt andere Applicative Functors, die keine Monaden sind (siehe diese SO-Frage) und die leicht zu finden sind. Eine alternative Schnittstelle für Monaden zu haben ist nett und alles, aber manchmal ist es ineffizient, kompliziert oder sogar unmöglich, eine Monade zu erstellen, und dann benötigen Sie Applicative Functors.
Haftungsausschluss: Die Herstellung von Anwendungsfunktionen kann auch ineffizient, kompliziert und unmöglich sein. Wenden Sie sich im Zweifelsfall an Ihren lokalen Kategorietheoretiker, um Informationen zur korrekten Verwendung von Anwendungsfunktionen zu erhalten.
quelle
Nach meiner Erfahrung sind Applikative Funktoren aus folgenden Gründen großartig:
Bestimmte Arten von Datenstrukturen lassen mächtige Arten von Kompositionen zu, können aber nicht wirklich zu Monaden gemacht werden. Tatsächlich fallen die meisten Abstraktionen in der funktionalen reaktiven Programmierung in diese Kategorie. Während wir technisch in der Lage sein könnten, zB
Behavior
(akaSignal
) eine Monade zu machen, kann dies normalerweise nicht effizient durchgeführt werden. Applikative Funktoren ermöglichen es uns, immer noch kraftvolle Kompositionen zu haben, ohne die Effizienz zu beeinträchtigen (zugegebenermaßen ist es manchmal etwas schwieriger, ein Applikativ als eine Monade zu verwenden, nur weil Sie nicht so viel Struktur haben, mit der Sie arbeiten können).Das Fehlen einer Datenabhängigkeit in einem anwendbaren Funktor ermöglicht es Ihnen, z. B. eine Aktion zu durchlaufen, um nach allen möglichen Effekten zu suchen, ohne dass die Daten verfügbar sind. Sie können sich also ein "Webformular" -Anwendungsprodukt vorstellen, das wie folgt verwendet wird:
userData = User <$> field "Name" <*> field "Address"
und Sie könnten eine Engine schreiben, die alle verwendeten Felder durchsucht und in einem Formular anzeigt. Wenn Sie die Daten zurückerhalten, führen Sie sie erneut aus, um die erstellten zu erhalten
User
. Dies kann nicht mit einem einfachen Funktor (weil er zwei Formen zu einer kombiniert) oder einer Monade durchgeführt werden, da man mit einer Monade Folgendes ausdrücken könnte:userData = do name <- field "Name" address <- field $ name ++ "'s address" return (User name address)
Dies kann nicht gerendert werden, da der Name des zweiten Felds nicht bekannt ist, ohne dass bereits die Antwort des ersten Felds vorliegt. Ich bin mir ziemlich sicher, dass es eine Bibliothek gibt, die diese Formularidee umsetzt - ich habe meine eigene ein paar Mal für dieses und jenes Projekt gerollt.
Das andere schöne an anwendungsbezogenen Funktoren ist, dass sie komponieren . Genauer gesagt, der Kompositionsfunktor:
newtype Compose f g x = Compose (f (g x))
ist anwendbar, wann immer
f
undg
sind. Das Gleiche gilt nicht für Monaden, die die gesamte Geschichte der Monadentransformatoren hervorgebracht haben, die auf unangenehme Weise kompliziert ist. Applikatoren sind auf diese Weise super sauber und es bedeutet, dass Sie die Struktur eines Typs aufbauen können, den Sie benötigen, indem Sie sich auf kleine zusammensetzbare Komponenten konzentrieren.Kürzlich wurde die
ApplicativeDo
Erweiterung in GHC veröffentlicht, mit der Sie diedo
Notation mit Applikativen verwenden können, wodurch ein Teil der Komplexität der Notation verringert wird, solange Sie keine monadischen Dinge tun.quelle
Ein gutes Beispiel: Anwendungsanalyse.
Siehe [real world haskell] ch16 http://book.realworldhaskell.org/read/using-parsec.html#id652517
Dies ist der Parser-Code mit Do-Notation:
-- file: ch16/FormApp.hs p_hex :: CharParser () Char p_hex = do char '%' a <- hexDigit b <- hexDigit let ((d, _):_) = readHex [a,b] return . toEnum $ d
Mit dem Funktor wird es viel kürzer :
-- file: ch16/FormApp.hs a_hex = hexify <$> (char '%' *> hexDigit) <*> hexDigit where hexify a b = toEnum . fst . head . readHex $ [a,b]
'Heben' kann die zugrunde liegenden Details eines sich wiederholenden Codes verbergen. Dann können Sie einfach weniger Wörter verwenden, um die genaue und genaue Geschichte zu erzählen.
quelle
char '%' >> liftM (toEnum . fst . head . readHex) (replicateM 2 hexDigit)
.count
Kombinator vonParsec
und wechseln Sie zurück zum Anwendungsstil:toEnum . fst . head . readHex <$> (char '%' >> count 2 hexDigit)
Ich würde auch einen Blick auf nehmen vorschlagen diese
Am Ende des Artikels gibt es ein Beispiel
import Control.Applicative hasCommentA blogComments = BlogComment <$> lookup "title" blogComments <*> lookup "user" blogComments <*> lookup "comment" blogComments
Dies zeigt verschiedene Merkmale des anwendbaren Programmierstils.
quelle