In Scala habe ich nach und nach meine Java / C-Gewohnheit verloren, auf kontrollflussorientiertes Denken zu denken, und habe mich daran gewöhnt, das Objekt, an dem ich interessiert bin, zuerst zu bekommen und dann normalerweise etwas wie ein match
oder a map()
oder foreach()
für Sammlungen. Ich mag es sehr, weil es sich jetzt wie eine natürlichere und zielgerichtetere Art und Weise anfühlt, meinen Code zu strukturieren.
Nach und nach wünschte ich, ich könnte den gleichen Weg für Bedingungen programmieren; h., erhalte zuerst einen booleschen Wert und dann match
, um verschiedene Dinge zu tun. Ein vollwertiges match
scheint jedoch für diese Aufgabe etwas übertrieben zu sein.
Vergleiche:
%Vor%vs. was ich mit Stil näher an Java schreiben würde:
%Vor% Dann erinnerte ich mich an Smalltalks ifTrue:
und ifFalse:
Nachrichten (und Varianten davon). Wäre es möglich, so etwas in Scala zu schreiben?
mit Varianten:
%Vor% Könnte dieser Stil außerdem einfachen Zwei-Zustands-Typen wie Option
zur Verfügung gestellt werden? Ich weiß, dass die idiomatische Methode, Option
zu verwenden, darin besteht, sie als Sammlung zu behandeln und filter()
, map()
, exists()
darauf anzurufen, aber am Ende finde ich oft, dass ich etwas doX
wenn es definiert ist und% doY
wenn nicht. Etwas wie:
Für mich sieht das (noch?) besser aus als ein vollwertiges match
.
Ich stelle eine grundlegende Implementierung zur Verfügung, die ich entwickelt habe; Allgemeine Kommentare zu diesem Stil / Technik und / oder besseren Implementierungen sind willkommen!
Erstens: Wir können else
wahrscheinlich nicht wiederverwenden, da es ein Schlüsselwort ist, und die Verwendung der Backticks, um zu erzwingen, dass es als Bezeichner betrachtet wird, ist ziemlich hässlich, also verwende ich stattdessen otherwise
.
Hier ist ein Implementierungsversuch. Verwenden Sie zunächst das Muster "pimp-my-library", um ifTrue
und ifFalse
zu Boolean
hinzuzufügen. Sie werden am Rückgabetyp R
parametrisiert und akzeptieren einen einzelnen by-name-Parameter, der ausgewertet werden soll, wenn die angegebene Bedingung erfüllt ist. Aber dabei müssen wir einen otherwise
-Aufruf zulassen. Wir geben also ein neues Objekt mit dem Namen Otherwise0
zurück (warum 0 später erklärt wird), das ein mögliches Zwischenergebnis als Option[R]
speichert. Es ist definiert, wenn die aktuelle Bedingung ( ifTrue
oder ifFalse
) realisiert wird und ansonsten leer ist.
Im Moment funktioniert das und lässt mich schreiben
%Vor% Aber ohne die folgende otherwise
-Klausel kann natürlich kein Wert vom Typ R
zurückgegeben werden. Also hier ist die Definition von Otherwise0
:
Er wertet sein übergebenes genanntes Argument genau dann aus, wenn das Zwischenergebnis, das es aus dem vorhergehenden ifTrue
oder ifFalse
erhalten hat, undefiniert ist, was genau das ist, was gewünscht wird. Der Typ parametrization [S >: R]
hat den Effekt, dass S
als der spezifischste gemeinsame Supertyp des tatsächlichen Typs der benannten Parameter gilt, so dass zum Beispiel r
in diesem Code einen abgeleiteten Typ Fruit
hat:
Mit dem apply()
Alias können Sie sogar den Methodennamen otherwise
für kurze Codeabschnitte überspringen:
Schließlich ist hier der entsprechende Zuhälter für Option
:
Beachten Sie, dass wir nun auch Otherwise1
benötigen, damit wir den nicht ausgepackten Wert nicht nur an das ifSome
Funktionsargument übergeben können, sondern auch an das Funktionsargument eines otherwise
nach einem ifNone
.
Vielleicht sehen Sie sich das Problem zu genau an. Sie wären wahrscheinlich mit dem Pfahlbetreiber besser dran:
%Vor%Jetzt können Sie
%Vor%was zugegebenermaßen nicht ganz so elegant ist wie das, was Sie erreichen wollen, aber es hat den großen Vorteil, unglaublich vielseitig zu sein - jedes Mal, wenn Sie einen Streit (nicht nur mit Booleans) anführen wollen kann es benutzen.
Sie können Optionen auch bereits so verwenden, wie Sie möchten:
%Vor% Nur weil diese Dinge einen Rückgabewert haben können, heißt das nicht, dass Sie sie vermeiden müssen. Es braucht ein wenig, um sich an die Syntax zu gewöhnen; Dies kann ein guter Grund sein, eigene Implikationen zu erzeugen. Aber die Kernfunktionalität ist da. (Wenn Sie ein eigenes erstellen, betrachten Sie stattdessen fold[B](f: A => B)(g: => B)
; wenn Sie einmal damit vertraut sind, ist das Fehlen des dazwischen liegenden Schlüsselworts eigentlich ziemlich nett.)
Edit: Obwohl die |>
Notation für Pipe etwas Standard ist, bevorzuge ich tatsächlich use
als Methodenname, weil dann def reuse[B,C](f: A => B)(g: (A,B) => C) = g(a,f(a))
natürlicher erscheint.
Tags und Links scala match control-flow