Grobe Übersicht
In der funktionalen Programmierung ist ein Funktor im Wesentlichen eine Konstruktion, bei der gewöhnliche unäre Funktionen (dh solche mit einem Argument) auf Funktionen zwischen Variablen neuen Typs angehoben werden. Es ist viel einfacher, einfache Funktionen zwischen einfachen Objekten zu schreiben und zu verwalten und sie mit Funktoren anzuheben, als Funktionen zwischen komplizierten Containerobjekten manuell zu schreiben. Ein weiterer Vorteil besteht darin, einfache Funktionen nur einmal zu schreiben und sie dann über verschiedene Funktoren wiederzuverwenden.
Beispiele für Funktoren sind Arrays, "Vielleicht" - und "Entweder" -Funktoren, Futures (siehe z. B. https://github.com/Avaq/Fluture ) und viele andere.
Illustration
Betrachten Sie die Funktion, die den vollständigen Namen der Person aus dem Vor- und Nachnamen erstellt. Wir könnten es fullName(firstName, lastName)
als Funktion von zwei Argumenten definieren, was jedoch nicht für Funktoren geeignet wäre, die sich nur mit Funktionen eines Arguments befassen. Um Abhilfe zu schaffen, sammeln wir alle Argumente in einem einzigen Objekt name
, das nun zum einzigen Argument der Funktion wird:
// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName
Was ist nun, wenn wir viele Leute in einem Array haben? Anstatt die Liste manuell durchzugehen, können wir unsere Funktion einfach fullName
über die map
für Arrays mit kurzer Codezeile bereitgestellte Methode wiederverwenden :
fullNameList = nameList => nameList.map(fullName)
und benutze es gerne
nameList = [
{firstName: 'Steve', lastName: 'Jobs'},
{firstName: 'Bill', lastName: 'Gates'}
]
fullNames = fullNameList(nameList)
// => ['Steve Jobs', 'Bill Gates']
Das funktioniert immer dann, wenn jeder Eintrag in unserem nameList
ein Objekt ist, das sowohl Eigenschaften firstName
als auch lastName
Eigenschaften bietet . Aber was ist, wenn einige Objekte dies nicht tun (oder gar keine Objekte sind)? Um die Fehler zu vermeiden und den Code sicherer zu machen, können wir unsere Objekte in den Maybe
Typ einschließen (siehe z. B. https://sanctuary.js.org/#maybe-type ):
// function to test name for validity
isValidName = name =>
(typeof name === 'object')
&& (typeof name.firstName === 'string')
&& (typeof name.lastName === 'string')
// wrap into the Maybe type
maybeName = name =>
isValidName(name) ? Just(name) : Nothing()
Dabei Just(name)
ist ein Container nur gültige Namen und Nothing()
der spezielle Wert, der für alles andere verwendet wird. Anstatt jetzt zu unterbrechen (oder zu vergessen), um die Gültigkeit unserer Argumente zu überprüfen, können wir unsere ursprüngliche fullName
Funktion einfach mit einer anderen einzelnen Codezeile wiederverwenden (aufheben) , basierend auf der map
Methode, die diesmal für den Typ Vielleicht vorgesehen ist:
// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)
und benutze es gerne
justSteve = maybeName(
{firstName: 'Steve', lastName: 'Jobs'}
) // => Just({firstName: 'Steve', lastName: 'Jobs'})
notSteve = maybeName(
{lastName: 'SomeJobs'}
) // => Nothing()
steveFN = maybeFullName(justSteve)
// => Just('Steve Jobs')
notSteveFN = maybeFullName(notSteve)
// => Nothing()
Kategorietheorie
Ein Funktor in der Kategorietheorie ist eine Karte zwischen zwei Kategorien, die die Zusammensetzung ihrer Morphismen berücksichtigen. In einer Computersprache , ist die wichtigste Kategorie von Interesse der, dessen Objekte sind Typen (bestimmte Sätze von Werten), und deren Morphismen sind Funktionen f:a->b
von einem Typ a
in einen anderen Typ b
.
Nehmen a
wir zum Beispiel den String
Typ, b
den Zahlentyp und f
die Funktion, die eine Zeichenfolge in ihre Länge abbildet:
// f :: String -> Number
f = str => str.length
Hier wird a = String
die Menge aller Zeichenfolgen und b = Number
die Menge aller Zahlen dargestellt. In diesem Sinne repräsentieren beide a
und b
repräsentieren Objekte in der Set-Kategorie (die eng mit der Kategorie der Typen verwandt ist, wobei der Unterschied hier unwesentlich ist). In der Set-Kategorie sind Morphismen zwischen zwei Sets genau alle Funktionen vom ersten bis zum zweiten Set. Unsere Längenfunktion f
hier ist also ein Morphismus von der Menge der Zeichenketten in die Menge der Zahlen.
Da wir nur die Mengenkategorie betrachten, sind die relevanten Funktoren daraus Karten, die Objekte an Objekte und Morphismen an Morphismen senden, die bestimmte algebraische Gesetze erfüllen.
Beispiel: Array
Array
kann viele Dinge bedeuten, aber nur eines ist ein Functor - das Typkonstrukt, das einen Typ a
dem Typ [a]
aller Arrays des Typs zuordnet a
. Beispielsweise Array
ordnet der Funktor den Typ String
dem Typ [String]
(der Menge aller Arrays von Zeichenfolgen beliebiger Länge) und den Typ Number
dem entsprechenden Typ [Number]
(der Menge aller Arrays von Zahlen) zu.
Es ist wichtig, die Functor-Karte nicht zu verwechseln
Array :: a => [a]
mit einem Morphismus a -> [a]
. Der Funktor ordnet den Typ einfach als eine Sache der anderen a
dem Typ [a]
zu. Dass jeder Typ tatsächlich eine Reihe von Elementen ist, spielt hier keine Rolle. Im Gegensatz dazu ist ein Morphismus eine tatsächliche Funktion zwischen diesen Mengen. Zum Beispiel gibt es einen natürlichen Morphismus (Funktion)
pure :: a -> [a]
pure = x => [x]
Dies sendet einen Wert in das 1-Element-Array mit diesem Wert als Einzeleintrag. Diese Funktion ist nicht Teil des Array
Functor! Aus der Sicht dieses Funktors pure
ist nur eine Funktion wie jede andere, nichts Besonderes.
Auf der anderen Seite hat der Array
Functor seinen zweiten Teil - den Morphismus-Teil. Was einen Morphismus f :: a -> b
in einen Morphismus abbildet [f] :: [a] -> [b]
:
// a -> [a]
Array.map(f) = arr => arr.map(f)
Hier arr
ist jedes Array beliebiger Länge mit Werten vom Typ a
und arr.map(f)
ist das Array derselben Länge mit Werten vom Typ b
, dessen Einträge das Ergebnis der Anwendung f
auf die Einträge von sind arr
. Um es zu einem Funktor zu machen, müssen die mathematischen Gesetze der Zuordnung von Identität zu Identität und von Kompositionen zu Kompositionen gelten, die in diesem Array
Beispiel leicht zu überprüfen sind .