2016-06-10 18 views
3

Ich bin ziemlich neu in Scala und stolperte über ein kleines Problem, das mich stört. Lassen Sie uns sagen, dass eine Methode mit StandardparameterMethodenaufruf mit Optionswert oder Default-Parameter in Scala

def foo(v: Any = "default"): String = s"called with parameter '$v'" 

und eine Option val opt: Option[String]. Wie kann diese Methode mit dem Optionswert (falls definiert) oder dem Standardparameter aufgerufen werden? ich meinen trotz der offensichtlichen Lösung

val result = if (opt.isDefined) 
       from.here.to.foo(opt.get) 
      else 
       from.here.to.foo() 

und mit dem doppelten Methode mit (möglicherweise lange) Objektkette geben? Nicht zu erwähnen, mit mehr als einen optional/Standardparameter ...


Alles, was ich tun konnte, ist das nicht hilfreich Helfer

def definedOrDefault[A, B](opt: Option[A], f0: => B, f1: A => B): B = 
    if (opt.isDefined) f1(opt.get) else f0 

aber wenn keine Standardparameter in höherer Ordnung in der Lage zu erwähnen Funktionen ... das war's. Erinnert mich an die schlechten alten Zeiten mit Java, wo das Überladen von Methoden das gleiche Problem verursacht.

Antwort

3

Es scheint, als ob Sie zwei Wertbegriffe verwenden, die an einer Stelle optional sind, d. H. Optionale Parameter und Option. Sie mögen nicht gut zusammen spielen, vielleicht ist es besser, nur einen zu verwenden.

Wenn Sie immer nur den Wert Option an eine Methode übergeben oder nichts übergeben, um den Standardwert zu erhalten, sollten Sie möglicherweise die Funktion ändern, um die Option zu akzeptieren.

def foo(v: Option[String]) = { 
    val param = v getOrElse "default" 
    s"called with parameter '$param'" 
} 

Wenn Sie noch Standardparameter haben möchten, können Sie Signatur

def foo(v: Option[String] = None) 

Dieser Ansatz aber Sie benötigen, ändern Sie Ihre Parameter in Some wickeln, wenn Sie einen regulären Anruf tun z.B.

foo(Some("regular param")) 

aber es funktioniert gut, wenn Sie Option s verwenden. Sie können einfach weitere optionale Parameter hinzufügen. Hier

ist ein Beispiel

def foo(v1: Option[String] = None, v2: Option[Int] = None) = { 
    val param1 = v1 getOrElse "default" 
    val param2 = v2 getOrElse 42 
    s"'$param1', '$param2'" 
} 

foo() // "default", 42 

foo(v2 = Some(12)) // "default", 12 

foo(Some("asd"), Some(11)) // "asd", 11 

val optionFromSomewhere = Some("a") 
val anotherOptionFromSomewhere = Option.empty[Int] 
foo(optionFromSomewhere, anotherOptionFromSomewhere) // "a", 42 

Sie könnten auch implizite Konvertierung Any-Option vorstellen, dass Sie Some wegzulassen lassen würde, aber ich glaube nicht, es ist so eine gute Idee

implicit def any2option[A](e: A): Option[A] = Some(e) 
+0

Sie könnten sogar den Parameter "default" in diesem Fall auf 'None' setzen, um einige der gleichen Semantik beizubehalten. – wheaties

+0

Ja, das habe ich mir auch gedacht. Die Antwort war work in progress: p –

+0

Ich hatte auf eine Lösung gehofft, bei der die Methode unberührt bleiben kann, sie könnte Teil einer Bibliothek sein und außerhalb meiner Reichweite liegen. Und die Überprüfung der Parameter bricht zuerst den funktionalen Stil. Aber immer noch nett! – ABika

0

Sie können die in Option definierte Funktion map verwenden, um den darin enthaltenen Wert zu transformieren, falls dieser definiert ist. Das sieht wie folgt aus:

opt.map(foo) 
// same as 
opt.map(x => foo(x)) 

Diese Rückkehr wird entweder ein Option[String] wenn irgendein Wert innerhalb war, oder None wenn es None vorher war.Hier

ist das vollständige Beispiel aus dem REPL:

scala> def foo(v: Any = "default"): String = s"called with parameter '$v'" 
foo: (v: Any)String 

scala> Some("Hello") 
res0: Some[String] = Some(Hello) 

scala> res0.map(foo) 
res1: Option[String] = Some(called with parameter 'Hello') 

scala> val x: Option[String] = None 
x: Option[String] = None 

scala> x.map(foo) 
res2: Option[String] = None 

EDIT:

Um es mit einem Standardwert zu nennen, sollen Sie Ihre Option, bevor Sie Ihren Anruf übereinstimmen, da das Verfahren einen erwartet nicht Option Parameter.

val optOrDefault = opt getOrElse "default" 
foo(optOrDefault) 
+0

Ich denke, OP würde gerne "mit Parameter default" aufrufen, wenn die Option leer ist. Dies würde "opt map foo getOrElse foo()" erfordern, was etwas besser ist, aber zweimal "foo" eingeben muss und nicht auf weitere optionale Parameter skaliert werden kann. –

+0

Hmm, du magst recht haben. Ich werde meine Antwort verlängern, danke. –

1

Leichte Erweiterung @ Łukasz Antwort, die zu groß ist, um einen Kommentar zu passen:

Sie vermeiden können, die vorhanden Parameter in Some ohne gefährliche any2option einzuwickeln durch eine Spezial-Art zu schaffen:

sealed trait OptParam[+A] { def toOption: Option[A] } 
case class Param[+A](value: A) extends OptParam[A] { def toOption = Some(value) } 
case object NoParam extends OptParam[Nothing] { def toOption = None } 

object OptParam { 
    implicit def any2optParam[A](x: A): OptParam[A] = Param(x) 
} 

def foo(v1: OptParam[String] = NoParam, v2: OptString[Int] = NoParam) = { 
    val param1 = v1.toOption.getOrElse("default") 
    val param2 = v2.toOption.getOrElse(42) 
    s"'$param1', '$param2'" 
} 

foo("a") // "'a', '42'" 

Was sicherer macht, ist, dass OptParam sollte immer nur als Methode Parametertyp angezeigt, so dass die Umwandlung nicht ausgelöst wird, wo man es nicht erwartet.