Wie heißt eine Funktion, die kein Argument akzeptiert und nichts zurückgibt? [geschlossen]

80

Im Java 8- java.util.functionPaket haben wir:

  • Funktion : Nimmt ein Argument und erzeugt ein Ergebnis.
  • Verbraucher : Nimmt ein Argument, produziert nichts.
  • Lieferant : Nimmt kein Argument, produziert ein Ergebnis.
  • ... : Andere Fälle, die Primitive, 2 Argumente usw. behandeln ...

Aber ich muss den Fall " nimmt kein Argument, produziert nichts " behandeln.

Es gibt nichts dafür in java.util.functionnal.

Die Frage ist also:

Wie heißt eine Funktion, die kein Argument annimmt und nichts zurückgibt ?

In Java 8 wäre seine Definition:

@FunctionalInterface
public interface InsertANameHere {
    void execute();
}

Executor ist bereits vorhanden und hat einen anderen Zweck: " Ein Objekt, das übermittelte ausführbare Tasks ausführt ". Die Signatur stimmt nicht überein ( execute(Runnable):void) und ist nicht einmal eine funktionierende Schnittstelle .

Runnable existiert, ist aber stark mit dem Threading-Kontext verbunden:

  • Das Paket ist java.langnicht java.util.function.
  • Im Javadoc heißt es: " Die Runnable-Schnittstelle sollte von jeder Klasse implementiert werden, deren Instanzen von einem Thread ausgeführt werden sollen ."
  • Der Name "Runnable" deutet auf einen laufenden Code innerhalb eines Threads hin.
Superbob
quelle
28
„Aber es gibt nichts für‚kein Argument nimmt, produziert nichts‘ . “ - Runnable ?
user11153
11
Ich denke, dass das javadoc für Runnablezu diesem Zeitpunkt veraltet ist, da ein Runnable auch von anderen Klassen als Thread( Executorzum Beispiel) verwendet wird.
SpaceTrucker
13
@superbob Das heißt nicht, dass Runnables nur .run()von Threads sein kann. Tatsächlich werden sie sehr häufig für genau den in der Frage beschriebenen Zweck verwendet
blgt 20.03.15
5
@superbob Das war der ursprüngliche Zweck, aber seit Java 8 wurde es als funktionale Schnittstelle "nachgerüstet". Deshalb haben Sie nichts im java.util.function Paket gefunden.
user11153
5
Semi-Snark: ImpureFuntion, weil es sicher nur auf Nebenwirkungen beruht, sonst ist es ein No-Op. ( en.wikipedia.org/wiki/Pure_function#Impure_functions ) Serioser: Imperativ (etwas tun), das zumindest der Semantik von void execute () entspricht;
Kristian H

Antworten:

71

Javas Entscheidung, es so zu machen, mit einem eigenen Namen für jede Arität, war dumm. Es lohnt sich nicht, es zu emulieren. Wenn Sie dies jedoch aus Gründen der Konsistenz tun müssen oder wenn Sie sehr allgemeinen Bibliothekscode schreiben, sind Konrads Vorschläge gut. Ich könnte Procedurein den Ring werfen .

Die Verwendung eines pseudofunktionalen Paradigmas bedeutet nicht, dass normale Benennungsprinzipien aus dem Fenster verschwinden sollten. Interfaces sollten fast immer nach dem benannt werden, was sie tun , nicht nach einer generischen syntaktischen Idee. Wenn die Funktionen in einem Rückgängig-Stapel abgelegt werden, sollten sie benannt werden UndoFunction. Wenn sie von GUI-Ereignissen aufgerufen werden, sollten sie benannt werden GUIEventHandler. Freunde lassen Freunde keine schlechten Namenskonventionen aufrechterhalten.

Karl Bielefeldt
quelle
Procedureist auch in Ordnung. Für den Kontext entwickle ich eine generische Bibliothek, die mit dieser Art von "Struktur" umgehen muss.
Superbob
18
Ich mag Procedurevon meinen Pascal-Tagen. A Procedurehat Nebenwirkungen, a Functionnicht. Da Sie keinen Wert zurückgeben, kann dies nur zu Nebenwirkungen führen.
Spencer Rathbun
Ich bin möglicherweise geneigt, ProcedureRIR<T1,T2>für void proc(T1, int, T2)und auch für andere Typen zu verwenden - wahrscheinlich werden die meisten von ihnen auf Anfrage erstellt, anstatt zu versuchen, jede mögliche Kombination von Typen zu stopfen.
Superkatze
1
In der Tat fördert echte funktionale Programmierung im Allgemeinen nicht die Benennung des ungarischen Stils. Dies ist eher die Mentalität des OO-Paradigmas als die funktionale.
Slebetman
3
If the functions are placed into an undo stack, they should be named UndoFunction.Es gibt jedoch einen Unterschied zwischen der Benennung der Funktion und der Zuweisung eines anderen Typs. In funktionalem Stil würden Sie keine erstellen UndoFunctonArt , denn jetzt können Sie es nicht passieren zu flip, curry, compose, filter, map, usw. Meiner Meinung nach, dass der wahre Grund ist Java Entscheidungsfunktionen zu geben , mit verschiedenen arities verschiedene Namen dumm. Natürlich, wenn Sie Nebenwirkungen verwenden wollen, nennen Sie es wie auch immer; Sie haben die Komposition bereits aus dem Fenster geworfen und verwenden vermutlich auch keine Funktionen.
Doval
33

In der Java-Welt heißt es Runnable. In der C # -Welt heißt es Action.

Es gibt jedoch einen besseren Namen, der sich gut in eine größere Sicht der Dinge einfügt.

Die größere Sicht der Dinge wird später deutlich, wenn Sie entscheiden, dass Sie neben Ihrer parameterlosen Void-Funktionsschnittstelle auch ähnliche Funktionsschnittstellen benötigen, die ein, zwei oder mehr Argumente akzeptieren oder einen Wert zurückgeben. In diesem Fall möchten Sie, dass die Namen aller dieser Entitäten isomorph sind und miteinander korrespondieren.

In Java habe ich also meine eigenen Funktionsschnittstellen, die ich Procedures nenne und die wie folgt definiert sind:

public interface Procedure
{
    void invoke();
}

public interface Procedure1<T1>
{
    void invoke( T1 argument1 );
}

... (du bekommst das Bild.)

Und ich habe auch einen ähnlichen Satz von Schnittstellen namens Functions, die auf ähnliche Weise definiert sind, wobei der erste generische Parameter der Rückgabetyp ist:

public interface Function<R>
{
    R invoke();
}

public interface Function1<R,T1>
{
    R invoke( T1 argument1 );
}

Mein Punkt hier ist also, dass dies Procedureein sehr guter Name ist, weil er sich gut in eine größere Sicht der Dinge einfügt. Wenn Sie sich später für ähnliche funktionale Schnittstellen mit Methoden entscheiden, die Argumente akzeptieren oder einen Wert zurückgeben, werden Sie darauf stoßen.

ANMERKUNG: Ich stimme im Grundemit Karl Bielefeldts Behauptung überein , dass "normale Benennungsprinzipien [nicht] aus dem Fenster gehen sollten" und dass "Schnittstellen fast immer nach dem benannt werden sollten, was sie tun, nicht nach einer generischen syntaktischen Idee". Aber beachte, dass selbst er "fast immer" zulässt. Manchmal werden (im Wesentlichen anonyme) Verfahren und Funktionen benötigt, und das fragt das OP, und darauf antworte ich.

Änderung 2017-11-10:

Sie könnten fragen, warum Function1<R,T1>statt Function1<T1,R>? Es könnte in beide Richtungen gehen, aber ich bevorzuge Rückgabewerte auf der linken Seite, da ich die Namenskonvention "Konvertieren von" (Ziel von Quelle) im Gegensatz zur Namenskonvention "Konvertieren nach" (Quelle zu) befolgen möchte Konvention. (Was eher ein Unfall als eine Konvention ist, in dem Sinne, dass höchstwahrscheinlich niemand darüber nachgedacht hat, denn wenn sie überhaupt darüber nachgedacht hätten, wären sie zu der Konvention gekommen, von der aus man konvertiert. )

Ich habe darüber in Joel Spolksy - Falschen Code falsch aussehen lassen gelesen. Es ist ein sehr langer Artikel, den ich in seiner Gesamtheit empfehle. Wenn Sie jedoch direkt zum vorliegenden Fall springen möchten, suchen Sie nach 'TypeFromType', aber nach ' Geben Sie die TL; DR ein, die Idee ist, dass dies myint = intFromStr( mystr )viel besser ist als myint = strToInt( mystr ), da im ersten Fall die Namen der Typen nahe an den zugeordneten Werten liegen, sodass Sie leicht erkennen können, dass das 'int' mit dem 'int' und übereinstimmt das 'str' stimmt mit dem 'str' überein.

Daher neige ich dazu, die Dinge so zu ordnen, wie sie im Code erscheinen werden.

Mike Nakis
quelle
1
Diese Lösung ist wirklich gut, sie scheint sogar noch kugelsicherer zu sein als die von Java, Supplier, Consumer, BiFunction, ..., da sie nur zwei Konzepte enthält. Es erinnert mich an die Antwort von Karl Bielefeldt über " Javas Entscheidung, es so zu machen, mit einem eigenen Namen für jede dumme Sache ". Der einzige "Nachteil" besteht darin, dass primitive Typen und ihre Kombinationen (lustige Dinge wie DoubleToLongFunction, ToLongBiFunction, ...) nicht verarbeitet werden. Aber Primitive sind ein weiteres
Problem
Ja. Wenn Sie damit beginnen, Generika durch Grundelemente zu ersetzen, bedeutet dies, dass Sie sich wirklich für die Leistung interessieren. Es ist also in Ordnung, wenn Sie von der hier vorgestellten Konvention abweichen und stark angepasste Namen für eine stark angepasste Leistungsverbesserung verwenden.
Mike Nakis
1
Wenn ich eine zweite Antwort akzeptieren könnte, würde ich Ihre anhand der Vorschläge für Verfahren N , Funktion N auswählen . Ich habe es immer noch positiv bewertet.
Superbob
Warum ist es nicht Function1<T1, R>?
ErikE
@ErikE Das ist eine sehr gute Frage, und ich habe meinen Beitrag geändert, um sie zu beantworten.
Mike Nakis
18

Warum nicht Command? Angesichts der Tatsache, dass es keine Daten akzeptiert und keine Daten zurückgibt, aber unter der Annahme, dass das Aufrufen einen gewissen Effekt hat (andernfalls wäre es wirklich ziemlich sinnlos), stelle ich mir vor, dass dies in etwa das einzige ist, was es tun kann - eine Aktion auslösen, etwas bewirken.

Apropos, es gibt auch einen allgemeinen ActionDelegaten in .NET. Im Gegensatz zu Java Consumerkann es 0 bis 16 Argumente annehmen. Mit anderen Worten, die einfachste Version benötigt keine - siehe MSDN .

Und da der Name nicht bedeutet, dass es etwas zu "konsumieren" gibt, scheint es auch eine gute Namenswahl zu sein.

Konrad Morawski
quelle
1
Commandist gut. Es kann mit dem Befehlsmuster verwechselt werden , aber es könnte die Lösung sein.
Superbob
7
Ich mag Actionmehr als Command. Ein Befehl klingt eher nach etwas, das empfangen und ausgewertet wird, während eine Aktion auf ihre Nebenwirkungen hin ausgeführt wird.
Bergi,
7
Ähm ... wie kann es nicht ein Kommandomuster sein? Sie haben genau das Command-Objekt erstellt! Es hat sogar traditionell benannte execute()Methode. 0_o '
hijarian
1
Ich mag keinen Vorschlag für eine Aktion, da dies eine übliche (und gute) Swing-Schnittstelle ist. Befehl ist vernünftig.
user949300
@ user949300 aber es kommt auf den Kontext an - Java ist eine große Welt, die Sie sehen, ich bin ein Android-Entwickler (jetzt) ​​und ich habe keine J2EE-bezogenen Besonderheiten;) Ich stimme natürlich zu, dass die gewählte Namenskonvention nicht kollidieren sollte mit der, die Ihr Framework verwendet.
Konrad Morawski,