Implementieren von ifTrue, ifFalse, ifSome, ifNone usw. in Scala, um zu vermeiden, wenn (...) und einfache Mustervergleiche

7

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?

%Vor%

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:

%Vor%

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!

    
Jean-Philippe Pellet 13.04.2011, 18:46
quelle

3 Antworten

13

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.

%Vor%

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 :

%Vor%

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:

%Vor%

Mit dem apply() Alias ​​können Sie sogar den Methodennamen otherwise für kurze Codeabschnitte überspringen:

%Vor%

Schließlich ist hier der entsprechende Zuhälter für Option :

%Vor%

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 .

    
Jean-Philippe Pellet 13.04.2011, 18:46
quelle
6

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.

    
Rex Kerr 13.04.2011 19:08
quelle
3

Warum benutzen Sie es nicht einfach so:

%Vor%

?

IMO, es ist sehr idiomatisch! :)

    
tuxSlayer 14.04.2011 09:12
quelle

Tags und Links