2016-08-12 1 views
4

Ich bin ein neues zu Scala aus Java Hintergrund, derzeit verwirrt über die beste Praxis unter Berücksichtigung Option[T].Was ist der Vorteil der Verwendung von Option.map über Option.isEmpty und Option.get?

Ich fühle mich wie mit Option.map ist nur mehr funktional und schön, aber das ist kein gutes Argument, um andere Leute zu überzeugen. Manchmal ist isEmpty check einfacher zu lesen. Gibt es objektive Vorteile oder sind es nur persönliche Präferenzen?

Beispiel:

Variation 1:

someOption.map{ value => 
    { 
    //some lines of code 
    } 
} orElse(foo) 

Variation 2:

if(someOption.isEmpty){ 
    foo 
} else{ 
    val value = someOption.get 
    //some lines of code 
} 

I ausgeschlossen absichtlich die Optionen fold oder Musteranpassung zu verwenden. Ich bin einfach nicht erfreut über die Idee, Option als eine Sammlung zu behandeln, und die Verwendung von Mustererkennung für eine einfache isEmpty Prüfung ist ein Missbrauch von Mustererkennung IMHO. Aber egal, warum ich diese Optionen nicht mag, ich möchte den Umfang dieser Frage auf die oben genannten zwei Varianten beschränken, wie sie im Titel genannt werden.

+0

Der Vorteil ist aus der Perspektive der funktionalen Programmierung. Sie können innerhalb des monadischen Wrappers bleiben und Ihren Fluss verketten. Dies mag im Falle von "Option" ähnlich erscheinen, aber wenn Sie an etwas wie "Zukunft" denken, wird "isEmpty" Ihnen überhaupt nicht helfen. –

+1

Betrachte auch die 'fold' Variante:' someOption.fold (foo) (value => ???) '. Und die 'match'-Variante:' someOption match {case Some (value) => ??? Fall None => foo} ' – Kolmar

+0

Es gibt auch eine dritte Variante, die ich persönlich bevorzuge. 'someOption match {case Some (Objekt) => ... case None => ...}' – mgosk

Antwort

2

mit Musterabgleich für eine einfache isEmpty Prüfung ist ein Missbrauch von Pattern-Matching IMHO

Wenn Sie nur wollen eine isEmpty Scheck, isEmpty/isDefined völlig in Ordnung ist. Aber in Ihrem Fall möchten Sie auch den Wert erhalten. Und die Verwendung eines Musterabgleichs ist kein Missbrauch; es ist genau der grundlegende Anwendungsfall. get verwenden, können sehr leicht zu Fehlern machen wie zu vergessen isDefined oder machen die falsche Prüfung zu überprüfen:

if(someOption.isEmpty){ 
    val value = someOption.get 
    //some lines of code 
} else{ 
    //some other lines 
} 

Hoffentlich Tests fangen würde, aber es gibt keinen Grund für „hoffentlich“ zu begleichen.

Combinators (map und Freunde) sind besser als get aus dem gleichen Grund Mustervergleich ist: Sie erlauben Ihnen nicht, diese Art von Fehler zu machen. Die Wahl zwischen Mustererkennung und Kombinator ist eine andere Frage. Allgemein werden Kombinatoren bevorzugt, weil sie besser zusammensetzbar sind (wie Yuvals Antwort erklärt). Wenn Sie etwas tun wollen, das von einem einzigen Kombinator abgedeckt wird, würde ich sie im Allgemeinen wählen. Wenn Sie eine Kombination wie map ... getOrElse oder eine fold mit mehrzeiligen Verzweigungen benötigen, kommt es auf den jeweiligen Fall an.

+0

Danke für das Aufzeigen des Fehlers im Code, korrigiert. "Missbrauch" kann ein starkes Wort sein, ich meinte eher nicht sehr idomatisch, wie von [ScalaDoc] (http://www.scala-lang.org/api/current/index.html#scala.Option) betont. Dennoch scheint es ein gutes praktisches Argument zu sein. Vielleicht möchten Sie den Fall einbeziehen, wo es in der Antwort schief gehen könnte? –

+0

Scaladoc sagt, es ist weniger idiomatisch als die Verwendung von Kombinatoren (im Wesentlichen aus Gründen, die in Yuvals Antwort erklärt wurden), aber "get" ist weniger idiomatisch als Mustervergleich. Es wird nur nicht einmal auf dieser Seite erwähnt. –

1

Es scheint ähnlich zu Ihnen im Fall von Option, aber betrachten Sie einfach den Fall von Future. Sie werden nicht in der Lage sein, mit dem Wert der Zukunft zu interagieren, nachdem Sie die Future Monade verlassen haben.

import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.Promise 
import scala.util.{Success, Try} 

// create a promise which we will complete after sometime 
val promise = Promise[String](); 

// Now lets consider the future contained in this promise 
val future = promise.future; 

val byGet = if (!future.value.isEmpty) { 
    val valTry = future.value.get 
    valTry match { 
    case Success(v) => v + " :: Added" 
    case _ => "DEFAULT :: Added" 
    } 
} else "DEFAULT :: Added" 

val byMap = future.map(s => s + " :: Added") 

// promise was completed now 
promise.complete(Try("PROMISE")) 


//Now lets print both values 

println(byGet) 
// DEFAULT :: Added 

println(byMap) 
// Success(PROMISE :: Added) 
5

Gibt es objektive Vorteile, oder ist es nur persönliche Präferenz?

Ich denke, es gibt eine dünne Linie zwischen objektiven Vorteilen und persönlichen Vorlieben. Du kannst keinen glauben machen, dass es eine absolute Wahrheit gibt. Der größte Vorteil, den man aus der Verwendung der monadischen Natur von Scala-Konstrukten erhält, ist Zusammensetzung. Die Möglichkeit, Operationen miteinander zu verketten, ohne sich um den internen Wert "Sorgen machen" zu müssen, ist nicht nur mit Option[T], sondern auch mit Future[T], Either[A, B] und dazwischen und zurück (siehe auch Monad Transformers) möglich.

Lassen Sie uns versuchen zu sehen, wie die Verwendung vordefinierter Methoden auf Option[T] kann mit Kontrollfluss helfen. Betrachten Sie zum Beispiel einen Fall, in dem Sie eine Option[Int] haben, die Sie nur dann multiplizieren möchten, wenn sie größer als ein Wert ist, andernfalls geben Sie -1 zurück. Im Imperativ Ansatz, erhalten wir:

val option: Option[Int] = generateOptionValue 

var res: Int = if (option.isDefined) { 
    val value = option.get 
    if (value > 40) value * 2 else -1 
} else -1 

Sammlungen Stil Methode auf Option Verwendung wäre ein Äquivalent wie folgt aussehen:

val result: Int = option 
    .filter(_ > 40) 
    .map(_ * 2) 
    .getOrElse(-1) 

Werfen wir nun einen Fall für Komposition betrachten. Nehmen wir an, wir haben eine Operation, die eine Ausnahme auslösen könnte. Zusätzlich kann diese Operation einen Wert ergeben oder nicht. Wenn es einen Wert zurückgibt, möchten wir eine Datenbank mit diesem Wert abfragen, andernfalls geben wir eine leere Zeichenfolge zurück.

Ein Blick auf die zwingende Ansatz mit einem try-catch Block:

var result: String = _ 
try { 
    val maybeResult = dangerousMethod() 
    if (maybeResult.isDefined) { 
    result = queryDatabase(maybeResult.get) 
    } else result = "" 
} 
catch { 
    case NonFatal(e) => result = "" 
} 

Nun wollen wir zusammen mit scala.util.Try betrachten mit einem Option[String] und Komponieren beide zusammen:

val result: String = Try(dangerousMethod()) 
    .toOption 
    .flatten 
    .map(queryDatabase) 
    .getOrElse("") 

Ich denke, das kocht schließlich nach unten zu welches Ihnen helfen kann, klaren Kontrollfluß Ihrer Betriebe zu verursachen. Gewöhnen Sie sich an die Arbeit mit Option[T].map statt Option[T].get wird Ihren Code sicherer machen.

Zum Schluss glaube ich nicht, dass es eine einzige Wahrheit gibt. Ich glaube, dass die Komposition zu einem schönen, lesbaren Nebeneffekt bei der Zurückstellung von sicherem Code führen kann und ich bin dafür. Ich denke, der beste Weg, anderen Menschen zu zeigen, was du fühlst, ist, ihnen Beispiele zu geben, wie wir es gerade gesehen haben, und ihnen die Kraft zu geben, die sie mit diesen Werkzeugsets nutzen können.

Verwandte Themen