2014-11-05 4 views
6

Ich habe folgende für Yield-Schleife, die in einem boolean nimmt und soll entweder einig (string) oder None ergeben, auf der boolean abhängig:Kann eine Scala für Rendite keine zurückgeben, wenn ich eine Option dazu gebe?

val theBoolean = false 

val x: Option[String] = 
for { 
    theArg <- theBoolean 
} yield { 
    if (theArg) { 
    "abc" 
    } else { 
    None 
    } 
} 

Dies funktioniert gut, wenn theBoolean tatsächlich eine Boolesche wie false ist. Allerdings, wenn ich wollte in einem passiere Option[Boolean]:

val theBoolean = Some(false) 

es wie Scala scheint automatisch wendet einen Teil() Wrapper zur Keiner Rückkehr - ich eine Beschwerde erhalten, dass „Expression von Typ Option [Serializable] nicht konform zum erwarteten Typ Option [String] "(wobei None nicht die Serialisable ist). Die Ausbeute ist vollkommen zufrieden mit der gleichen Zeichenfolge Rückkehr aber (es keine Option werden [Option [Zeichenfolge]]

Wie würde ich zurückgeben Keiner in diesem Fall?

+1

Ich entdecken, dass None.get tatsächlich macht den Compiler glücklich. Aber es scheint komisch, es so zu benutzen. – Nathan

+0

Was versucht dieser Code zu erreichen? –

+0

Eine Option wird übergeben - unter bestimmten Bedingungen möchte ich eine Zeichenfolge daraus extrahieren, aber unter anderen Bedingungen möchte ich, dass sie Keine ist. – Nathan

Antwort

13

A für Verständnis ist nur syntaktischer Zucker für eine Reihe von flatMap, map und filter. Lassen Sie uns Ihren Code desugar dann:

val theBoolean = Some(false) 

val x = theBoolean.map { theArg => 
    if (theArg) { 
    "abc" 
    } else { 
    None 
    } 
} 

Wie Sie sehen können, sind Sie nur den Wert der Option Abbildung über, so dass Sie entweder zurückgeben Some(abc), Some(None) oder None (bei theBoolean ist bereits None) .

Die kleinste gemeinsame Art von None und "abc"java.Serializable ist, das so ist, warum die Art der x als Option[Serializable] gefolgert wird, die als bedeutungslos als Option[Any].

Mögliche Lösungen sind:

  • unter Verwendung eines flatMap

    theBoolean.flatMap(theArg => if (theArg) Some("abc") else None) 
    

    oder sogar kürzer

    theBoolean.flatMap(if (_) Some("abc") else None) 
    
  • Filterung und Mapping

    theBoolean.withFilter(identity).map(_ => "abc") 
    

Wo ich identity verwendet, da Sie den Wert selbst testen.

Offensichtlich können Sie immer den syntaktischen Zucker durch ein für Verständnis vorgesehen nutzen, obwohl es eigentlich keinen Unterschied in diesem Fall macht

for { 
    theArg <- theBoolean 
    if theArg 
} yield "abc" 
+0

Vielen Dank, die Erklärung Panne war super hilfreich! – Nathan

1

Du hast Recht, alles innerhalb der yield Block ist in eine Option eingepackt (so funktioniert das Verständnis für Optionen) Im Allgemeinen beschreiben for-comprehensions, was mit dem Inhalt geschehen soll, der innerhalb der Monade gefunden werden kann, auf der das Verständnis angewendet wird, aber das Ende Ergebnis (für die Welt außerhalb der yield Block) ist immer noch eine Monade des gleichen Typs (zB Option, Try oder List).

Auf noch allgemeiner Hinweis: Es gibt eine Menge Beschreibung über was eine Monade ist. Du kannst davon ausgehen, dass eine Monade diese berüchtigte Schrödinger-Box ist und du überlegst, was mit dieser versteckten Katze passieren könnte, aber all das bleibt immer noch möglich, weil die Box noch nicht offen ist.

Eine mögliche Art und Weise zu tun, was Sie wollen:

val theBoolean = false 

val x: Option[String] = 
for { 
    theArg <- theBoolean if theArg 
} yield { 
    "abc" 
} 
0

Statt eines für das Verständnis es klingt wie Sie eine flatMap statt ein für das Verständnis wollen.

scala> Some(false).flatMap(if (_) Some("abc") else None) 
res4: Option[String] = None 
Verwandte Themen