Funktionswerte
fun F/A, fun M:F/A, anonyme Funs mit erfassten Variablen (Closures),
benannte Funs (fun Name(...) -> ... end), Aufrufe von Funktionswerten
(F(Args)) und dynamische Aufrufe (M:F(Args), apply/2,3). Die Fakten
stammen aus den festgelegten maint-29-Quellen (erts/emulator/beam/utils.c
erts_cmp, erl_printf_term.c, erl_lint) und aus Proben mit OTP 29.1.1.
Werte
| Quelle | Wert | Aufrufe betreten |
|---|---|---|
fun f/1 | Lokale Fun dieses Moduls; jedes fun f/1 eines Moduls ist derselbe Wert | f/1 dieses Moduls, exportiert oder nicht |
fun m:f/1 | Externe Fun, die m:f/1 benennt, auch für das aktuelle Modul | m:f/1, wenn ein Modul des Programms es exportiert |
fun(X) -> ... end | Lokale Fun, die die Variablen erfasst, die sie von ihrem Erzeuger liest; jede Auswertung baut einen neuen Wert | Die eigene erzeugte Funktion der Fun |
fun Name(X) -> ... end | Dasselbe, wobei Name innerhalb ihrer Klauseln an die Fun gebunden ist | Die eigene erzeugte Funktion der Fun |
fun M:F/A mit Variablen | Externe Fun, gebaut bei der Auswertung; gleich dem literalen fun m:f/1, das sie benennt | m:f/1, wenn ein Modul des Programms es exportiert |
fun F/Amuss eine Funktion des Moduls benennen (function F/A undefined) oder ein automatisch importiertes Builtin:fun is_atom/1ist die externe Funerlang:is_atom/1. Funs von Builtins rufen diese über die Builtin-Brücke auf; ein literalesfun erlang:F/Aeiner Funktion außerhalb des Katalogs (fun erlang:apply/2) meldet die nicht verfügbare Fähigkeitdynamic calls.F(Args)wertetFaus, dann die Argumente von links nach rechts, und prüft dann den Wert: Eine Nicht-Funktion löst{badfun, F}aus, eine andere Stelligkeit{badarity, {F, Args}}(vor dem Modul geprüft), eine externe Fun, deren Funktion nichts im Programm exportiert,undef. Ein Aufruf in Endposition ist ein Endaufruf, wie bei benannten Funktionen.is_function/1,2sind für Funs in Rümpfen und Guards wahr.
Dynamische Aufrufe
M:F(Args)mit einer Variablen (oder einem beliebigen Ausdruck) als Modul oder Funktion wertet das Modul aus, dann die Funktion, dann die Argumente von links nach rechts. Ein Modul oder eine Funktion, das bzw. die kein Atom ist, löstbadargaus; eine Funktion, die kein Modul des Programms mit dieser Stelligkeit exportiert, löstundefaus, mit{M, F, Arity, []}an der Spitze des Stacktraces (OTP zeigt die Argumente;apply/3und Aufrufe externer funs zeigen sie ebenfalls). Ein Aufruf in Endposition ist ein Endaufruf.apply(Fun, Args)undapply(M, F, Args)(automatisch importiert, sofern das Modulapply/2,3nicht definiert oder den Import unterdrückt; aucherlang:apply/2,3) werten ihre Argumente aus und verlangen dann, dassArgseine echte Liste ist (badarg).apply/2ruft dannFunwieF(Args)auf ({badfun, Fun},{badarity, {Fun, Args}});apply/3suchtM:F/length(Args)wie oben, sodass mehr als 255 Argumenteundefauslösen. Beide sind in Endposition Endaufrufe und in Guards nie zulässig.- Zur Laufzeit über ihren Namen erreichte
erlang:apply/2,3(spawn(erlang, apply, [F, Args]),M:apply(...)mit anerlanggebundenemM, eine Timer-Aktion) sind Builtins (builtins/erlang/apply.cpp), die bei der aufgerufenen Funktion mit denselben Fehlern fortsetzen. fun M:F/Amit Variablen löstbadargaus, sofernMundFnicht Atome sind undAkeine Ganzzahl in 0..255 ist; die Funktion muss erst existieren, wenn die Fun aufgerufen wird.- Die Suche verwendet die Hash-Indizes des Code-Servers über die Exporte des
Programms (Runtime), die für die
Lebensdauer des Programms registriert bleiben, sodass eine gefundene Funktion
während des Aufrufs nicht verschwinden kann. Ein Name, den kein Modul
exportiert, wird dann unter den Builtins gesucht, sodass
M:F(...),apply/3undfun M:F/Azur Laufzeit die Builtins des Brückenkatalogs erreichen. - Dienste.
CLAUSE_call_v1(context, module, function, arity)prüft die Namen und gibt denFrameDescriptordes Exports zurück (aus seinemExportDescriptor); die Argumente liegen bereits in den Registern.CLAUSE_apply_list_v1(context, fun, list, registers)undCLAUSE_call_list_v1(context, module, function, list, registers)kopieren zuerst die Liste in die Register. Alle drei geben nach dem Vermerken des Fehlers null zurück, und erzeugter Code übergibt dann wie beiF(Args)über den Markerclause.apply.CLAUSE_make_external_fun_v1baut die Fun einer externenFunDefinition, die der Code-Server einmal proM:F/Aerzeugt (CodeServer::external_fun).
Closures
- Jede Klausel beginnt mit dem Gültigkeitsbereich an der Stelle der Fun. Kopfvariablen sind neue Namen, die äußere verdecken (OTP warnt); ein innerhalb eines Kopfs wiederholter Name muss matchen. Guards lesen die Namen des Kopfs. Nichts, was eine Fun bindet, ist nach ihr sichtbar, und Namen aus case-Klauseln der umschließenden Funktion reichen nicht in sie hinein.
- Eine Fun erfasst jede äußere Definition, die ihre Köpfe, Guards oder Rümpfe lesen (verschachtelte Funs eingeschlossen), in Definitionsreihenfolge, so wie OTP die freien Variablen einer Funktion ordnet. Erfasste Werte werden bei der Auswertung des Fun-Ausdrucks in die Fun-Zelle kopiert, sodass sie die Rückkehr des Erzeugers und Speicherbereinigungen überleben und mit der Fun kopiert werden.
- Der Code ist eine private Funktion
-f/A-fun-N-(N zählt die Funs vonf/Ain Quelltextreihenfolge), die die Argumente der Fun und dann ihre erfassten Werte nimmt; Stacktraces zeigen diesen Namen mit der kombinierten Stelligkeit, wie die von OTP. Matcht keine Klausel, wirdfunction_clauseausgelöst. - Argumente plus erfasste Werte sind auf 255 begrenzt.
- Die Klauseln einer benannten Fun sehen
Nameals die Fun selbst: ein neuer Name, der einen äußeren verdeckt (OTP warnt), nie erfasst und nach der Fun nicht sichtbar. Eine gleichnamige Kopfvariable verdeckt ihn wiederum. Liest eine KlauselName, baut der Code der Fun den Wert beim Betreten aus ihren erfassten Werten, sodass er gleich (=:=) der aufgerufenen Fun ist, undName(...)ist ein gewöhnlicher Fun-Aufruf: In Endposition ist er ein Endaufruf und läuft mit konstantem Stack. - Eine anonyme Fun im Standardwert eines Record-Felds ist eine einzige Fun für jede Konstruktion, die den Standardwert verwendet (OTP expandiert eine Kopie pro Stelle).
Inline eingebettete funs
Bei -O2 (codegen/lowering_fun_inlining.cpp, lowering_list_loops.cpp,
lowering_map_loops.cpp) werden bei einem Aufruf eines an der Aufrufstelle
bekannten anonymen fun dessen Klauseln anstelle des Aufrufs abgesenkt,
ähnlich wie mit OTPs Option inline_list_funcs:
- Bekannte funs. Ein im Aufruf geschriebener fun (
(fun(X) -> ... end)(Y)oder das erste Argument einer Listenschleife unten) oder eine einmal an einen unbenannten anonymen fun (F = fun ... end) gebundene Variable, die einmal gelesen wird oder höchstens 40 Ausdrücke enthält. Im Code eines anderen fun wird der fun einer Variablen nur inline eingebettet, wenn alle erfassten Werte ebenfalls dort gebunden sind. Inline eingebettete Rümpfe werden höchstens vier Ebenen tief verschachtelt; binary- und Gleitkommaschleifen betten nichts inline ein. - Klauseln an Ort und Stelle. Die Argumente werden gegen die Klauseln und
guards des fun abgeglichen, und der Rumpf der passenden Klausel läuft im
Frame des Aufrufers. Passt keine Klausel, wird der fun aufgerufen, sodass
function_clausewie zuvor aus dem eigenen Code des fun stammt. Ein im Aufruf geschriebener fun wird nur auf diesem Pfad und den unten genannten Pfaden gebaut, die seinen Wert benötigen (Closure-Sinking); der fun einer Variablen wird wie zuvor dort gebaut, wo sie gebunden wird. - Listenschleifen.
lists:map/2,filter/2,foreach/2,all/2,any/2,flatmap/2,foldl/3,foldr/3,mapfoldl/3undmapfoldr/3aus Clauses eigenemlists-Modul mit einem bekannten fun der richtigen Stelligkeit werden zu einer Schleife über die Liste in der aufrufenden Funktion. Der fun-Aufruf jedes Elements wird inline eingebettet; Listen werden umgekehrt aufgebaut und am Ende einmal umgedreht;foldr/3undmapfoldr/3drehen zuerst ihre Eingabe um, sodass ihr fun die Elemente weiterhin vom letzten an sieht, nachdem ein unechtes Listenende bereits gescheitert ist. Wie die ersetzte Rumpfrekursion führen Listenschleifen nur dort Speicherbereinigungen durch, wo sie abgeben (alle 4,000 Elemente ohne andere Aufrufe), und in den Aufrufen ihres fun. - map-Faltungsschleifen.
maps:fold/3aus Clauses eigenemmaps-Modul mit einem bekannten fun der Stelligkeit 3 wird zu einer Schleife über die Einträge der map (die klassische map-Zelle oder einmal in eine tupelförmige Zelle kopierte Baumeinträge, wiemaps:fold/3sie liest), in Schlüsselreihenfolge wiemaps:fold/3. Ein map-Iterator oder ein Wert, der keine map ist, führt den tatsächlichenmaps-Aufruf aus, der ihn behandelt oder seinen eigenen Fehler auslöst. - Akkumulatoren im Eigenbesitz. Wenn jede Klausel eines Faltungs-fun
(
maps:fold/3,lists:foldl/3,lists:foldr/3) ihren Akkumulatorparameter aktualisiert zurückgibt,fun(K, V, Acc) -> Acc#{K := V + 1} end, wobeiAccsonst nirgends gelesen und von keinem fun erfasst wird, besitzt die Schleife das Ergebnis jedes Schritts: Ab dem zweiten Schritt erfolgt die Aktualisierung im Eigenbesitz und ändert die map direkt (map-Bäume). Der erste Schritt kopiert, da der ursprüngliche Akkumulator anderswo gehalten werden kann. - Gleiche Ergebnisse und Gründe. Werte, die Ausführungsreihenfolge des fun
und Fehlergründe entsprechen denen von
lists: Eine Nichtlisten-Eingabe fürmap/2,foldl/3,all/2,any/2und die unechten Listenenden vonfilter/2,foreach/2,foldr/3,mapfoldl/3,mapfoldr/3führen den tatsächlichenlists-Aufruf aus, der seinen eigenen Fehler auslöst; ein unechtes Listenende vonmap/2,foldl/3,all/2,any/2,flatmap/2löstfunction_clauseim Aufrufer aus. Ein nichtboolesches Prädikat löst{case_clause, V}(all/2,any/2) oder{bad_filter, V}(filter/2) aus, ein Ergebnis einesmapfold-fun, das kein Paar ist,{badmatch, V}. - Stacktraces von Ausnahmen innerhalb inline eingebetteter funs fehlen
die Frames des fun und des
lists- odermaps-Helfers (Unterschiede).
Darstellung
- Deskriptor. Jeder unterschiedliche Wert, den ein Modul erzeugt, wird zu
einem
abi::v1::FunDescriptorin der privaten Tabelle<prefix>.funsdes Moduls kompiliert (funs.hpp): Moduldeskriptor, Atom-Slots für Modul und Funktion, Erlang-Stelligkeit, Index, Extern-Flag und derFrameDescriptor, den ein Aufruf betritt (null für eine externe Fun außerhalb des Programms). Die Registrierung bindet sie anFunDefinitions (ModuleAtoms::funs,CodeServer::fun_definition); die Anzahl erfasster Werte einer lokalen Fun ist die Stelligkeit ihres Codes minus die der Fun. - Zelle. Geboxte Art
fun_closure: Header (Anzahl1 + n), ein nicht verfolgterconst FunDefinition *, dannnerfasste Werte. Durchlauf, Speicherbereinigung, Kopieren und Verifikation überspringen das Definitionswort, wie bei nativen Records. - Dienste.
CLAUSE_make_fun_v1(context, descriptor, captures, count, output)baut eine Fun.CLAUSE_apply_v1(context, fun, arity, arguments)prüft einen aufgerufenen Wert, vermerkt die obigen Fehler im Fehlerkanal (ErrorReason22-24) und hängt andernfalls die erfassten Werte an die Argumente an und gibt den zu betretendenFrameDescriptorzurück. - Inline-Closures. Die Runtime, die den Atom-Cache eines Moduls
beansprucht, füllt auch
ModuleDescriptor::fun_cachemit der Adresse der Definition jeder Fun; solange der Anspruch ihr eigener ist, alloziert der erzeugte Code die Fun-Zelle (Header, Definition, erfasste Werte) selbst per Bump-Allokation und ruftCLAUSE_make_fun_v1nur ohne Platz auf dem Heap oder in einer anderen Runtime auf.CLAUSE_apply_v1undCLAUSE_apply_list_v1lesen eine Fun der richtigen Stelligkeit (und die Argumentliste) direkt aus ihren Zellen, vor dem geprüften Pfad, der Fehler meldet. - Aufrufe. Die native Form übergibt die Argumente in einem Wort-Array und
ruft den Marker
clause.applymit dem Deskriptor auf;lower_framesmacht das Array zu den Prozessregistern und den Marker zu einem Eintritt oder, in Endposition, zu einer Endübergabe (Ausführungsmodell).
Vergleich und Ausgabe
- Funs werden nach Referenzen und vor Ports eingeordnet (Term-Ordnung).
- Lokale Funs werden vor externen Funs eingeordnet. Lokale Funs werden nach
Modul verglichen, dann nach Index, dann nach den erfassten Werten der Reihe
nach (
==vergleicht diese mit==); externe Funs nach Modul, Funktion und Stelligkeit. Gleiche Deskriptoren ergeben gleiche Werte, alsofun f/1 =:= fun f/1. erlang:display/1,~wund Berichte über nicht abgefangene Ausnahmen geben eine externe Fun alsfun m:f/1aus (Atome in Anführungszeichen wie beim Emulator) und eine lokale Fun als#Fun<m.Index.0>.
Unterschiede
Nummerierung, Ausgabe und Ordnung von Funs, die Erfassungsreihenfolge in
Comprehensions, undef-Stacktraces und Aufrufe von Modulen außerhalb des
Programms weichen von OTP ab, wie in Unterschiede
festgehalten.
Informationen über Funs
runtime/src/builtins/erlang/fun_info.cpp. erlang:fun_info/1 listet die
Einträge von OTP in der Reihenfolge von OTP auf: für eine lokale Fun pid,
module, new_index, new_uniq, index, uniq, name (-F/A-fun-N- für
eine anonyme Fun), arity, env (die erfassten Werte) und type; für
fun M:F/A module, name, arity, env ([]) und type. fun_info/2
gibt ein {Item, Value} zurück; ein unbekannter Eintrag ist badarg. pid ist
undefined, new_uniq der MD5 des Modulnamens und uniq dessen erste 24 Bit,
daher weichen sie von OTP ab. fun_to_list/1 ist der Text von ~w.
is_builtin/3 ist wahr für die Builtins der Runtime und erlang:apply/2,3.
Clause